Merge branch 'stable-6.2' into stable-6.3

* stable-6.2:
  Remove blank in maven.config
  DirCache: support option index.skipHash

Change-Id: If0bb5f1a317ab981e6bbf5671851f124b18ab8ca
diff --git a/DEPENDENCIES b/DEPENDENCIES
index be7ab17..ffb3d90 100644
--- a/DEPENDENCIES
+++ b/DEPENDENCIES
@@ -24,10 +24,10 @@
 maven/mavencentral/org.apache.sshd/sshd-osgi/2.8.0, Apache-2.0, approved, CQ23892
 maven/mavencentral/org.apache.sshd/sshd-sftp/2.8.0, Apache-2.0, approved, CQ23893
 maven/mavencentral/org.assertj/assertj-core/3.20.2, Apache-2.0, approved, clearlydefined
-maven/mavencentral/org.bouncycastle/bcpg-jdk15on/1.70, Apache-2.0, approved, #1713
-maven/mavencentral/org.bouncycastle/bcpkix-jdk15on/1.70, MIT, approved, clearlydefined
-maven/mavencentral/org.bouncycastle/bcprov-jdk15on/1.70, MIT, approved, #1712
-maven/mavencentral/org.bouncycastle/bcutil-jdk15on/1.70, MIT, approved, clearlydefined
+maven/mavencentral/org.bouncycastle/bcpg-jdk18on/1.71, Apache-2.0, approved, #2665
+maven/mavencentral/org.bouncycastle/bcpkix-jdk18on/1.71, MIT, approved, #2662
+maven/mavencentral/org.bouncycastle/bcprov-jdk18on/1.71, MIT AND LicenseRef-scancode-ocb-open-source-2013, approved, #2666
+maven/mavencentral/org.bouncycastle/bcutil-jdk18on/1.71, MIT, approved, #2663
 maven/mavencentral/org.eclipse.jetty.toolchain/jetty-servlet-api/4.0.6, EPL-2.0 OR Apache-2.0, approved, rt.jetty
 maven/mavencentral/org.eclipse.jetty/jetty-http/10.0.6, EPL-2.0 OR Apache-2.0, approved, rt.jetty
 maven/mavencentral/org.eclipse.jetty/jetty-io/10.0.6, EPL-2.0 OR Apache-2.0, approved, rt.jetty
@@ -35,29 +35,29 @@
 maven/mavencentral/org.eclipse.jetty/jetty-server/10.0.6, EPL-2.0 OR Apache-2.0, approved, rt.jetty
 maven/mavencentral/org.eclipse.jetty/jetty-servlet/10.0.6, EPL-2.0 OR Apache-2.0, approved, rt.jetty
 maven/mavencentral/org.eclipse.jetty/jetty-util/10.0.6, EPL-2.0 OR Apache-2.0, approved, rt.jetty
-maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.ant.test/6.2.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit
-maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.ant/6.2.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit
-maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.archive/6.2.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit
-maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.gpg.bc/6.2.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit
-maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.http.apache/6.2.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit
-maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.http.server/6.2.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit
-maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.http.test/6.2.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit
-maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.junit.http/6.2.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit
-maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.junit.ssh/6.2.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit
-maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.junit/6.2.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit
-maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.lfs.server.test/6.2.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit
-maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.lfs.server/6.2.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit
-maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.lfs.test/6.2.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit
-maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.lfs/6.2.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit
-maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.pgm.test/6.2.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit
-maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.pgm/6.2.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit
-maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.ssh.apache.agent/6.2.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit
-maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.ssh.apache.test/6.2.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit
-maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.ssh.apache/6.2.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit
-maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.ssh.jsch/6.2.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit
-maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.test/6.2.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit
-maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.ui/6.2.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit
-maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit/6.2.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit
+maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.ant.test/6.3.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit
+maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.ant/6.3.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit
+maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.archive/6.3.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit
+maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.gpg.bc/6.3.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit
+maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.http.apache/6.3.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit
+maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.http.server/6.3.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit
+maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.http.test/6.3.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit
+maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.junit.http/6.3.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit
+maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.junit.ssh/6.3.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit
+maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.junit/6.3.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit
+maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.lfs.server.test/6.3.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit
+maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.lfs.server/6.3.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit
+maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.lfs.test/6.3.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit
+maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.lfs/6.3.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit
+maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.pgm.test/6.3.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit
+maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.pgm/6.3.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit
+maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.ssh.apache.agent/6.3.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit
+maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.ssh.apache.test/6.3.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit
+maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.ssh.apache/6.3.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit
+maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.ssh.jsch/6.3.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit
+maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.test/6.3.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit
+maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.ui/6.3.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit
+maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit/6.3.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit
 maven/mavencentral/org.hamcrest/hamcrest-core/1.3, BSD-2-Clause, approved, CQ11429
 maven/mavencentral/org.mockito/mockito-core/2.23.0, Apache-2.0 AND MIT, approved, #958
 maven/mavencentral/org.objenesis/objenesis/2.6, Apache-2.0, approved, CQ15478
diff --git a/WORKSPACE b/WORKSPACE
index cce1316..fc626ce 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -263,32 +263,32 @@
     src_sha1 = "f35f5525a5d30dc1237b85457d758d578e3ce8d0",
 )
 
-BOUNCYCASTLE_VER = "1.70"
+BOUNCYCASTLE_VER = "1.71"
 
 maven_jar(
     name = "bcpg",
-    artifact = "org.bouncycastle:bcpg-jdk15on:" + BOUNCYCASTLE_VER,
-    sha1 = "062f72ec06f31a6c31a3f3355fce0384b21126d7",
-    src_sha1 = "9dee73ad926752ee3b421a7dc4531287166ccf36",
+    artifact = "org.bouncycastle:bcpg-jdk18on:" + BOUNCYCASTLE_VER,
+    sha1 = "d42ad9fe1b89246bb4ca2a45c0646bf6f6066013",
+    src_sha1 = "75b8faa0a36bd27207489c06a21a1958e208034e",
 )
 
 maven_jar(
     name = "bcprov",
-    artifact = "org.bouncycastle:bcprov-jdk15on:" + BOUNCYCASTLE_VER,
-    sha1 = "4636a0d01f74acaf28082fb62b317f1080118371",
-    src_sha1 = "6245e15dd47e5fc33cff275df61662e0a8e5958f",
+    artifact = "org.bouncycastle:bcprov-jdk18on:" + BOUNCYCASTLE_VER,
+    sha1 = "943e8d0c2bd592ad78759c39d6f749fafaf29cf4",
+    src_sha1 = "5d398e3d54c0b8d6e24b0d929f45a89939d78f02",
 )
 
 maven_jar(
     name = "bcutil",
-    artifact = "org.bouncycastle:bcutil-jdk15on:" + BOUNCYCASTLE_VER,
-    sha1 = "54280e7195a7430d7911ded93fc01e07300b9526",
-    src_sha1 = "4af4a6c92b8ea07885b27d8536b81b855497f4eb",
+    artifact = "org.bouncycastle:bcutil-jdk18on:" + BOUNCYCASTLE_VER,
+    sha1 = "57daa18bc93730eab46291d9b55a15480e013265",
+    src_sha1 = "2ece8feb18f69679f9b23d36007ac3b79eaf9e6d",
 )
 
 maven_jar(
     name = "bcpkix",
-    artifact = "org.bouncycastle:bcpkix-jdk15on:" + BOUNCYCASTLE_VER,
-    sha1 = "f81e5af49571a9d5a109a88f239a73ce87055417",
-    src_sha1 = "42f9de53a91b20bc06e88482c57fd97e5a84250d",
+    artifact = "org.bouncycastle:bcpkix-jdk18on:" + BOUNCYCASTLE_VER,
+    sha1 = "211bcae48a96c688ca215394d631eec2b874fff1",
+    src_sha1 = "c72fab9b147394495a0a2858730f62b262f8a844",
 )
diff --git a/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF
index e5569e1..87a5f5a 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: 6.2.1.qualifier
+Bundle-Version: 6.3.1.qualifier
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: JavaSE-11
 Import-Package: org.apache.tools.ant,
- org.eclipse.jgit.ant.tasks;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.junit;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.lib;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.util;version="[6.2.1,6.3.0)",
+ org.eclipse.jgit.ant.tasks;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.junit;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.lib;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.util;version="[6.3.1,6.4.0)",
  org.hamcrest.core;version="[1.1.0,3.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 77060bc..bcd87a7 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>6.2.1-SNAPSHOT</version>
+    <version>6.3.1-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 155bda4..0d3e40f 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: 6.2.1.qualifier
+Bundle-Version: 6.3.1.qualifier
 Bundle-RequiredExecutionEnvironment: JavaSE-11
 Import-Package: org.apache.tools.ant,
-  org.eclipse.jgit.storage.file;version="[6.2.1,6.3.0)"
+  org.eclipse.jgit.storage.file;version="[6.3.1,6.4.0)"
 Bundle-Localization: plugin
 Bundle-Vendor: %Bundle-Vendor
-Export-Package: org.eclipse.jgit.ant;version="6.2.1",
- org.eclipse.jgit.ant.tasks;version="6.2.1";
+Export-Package: org.eclipse.jgit.ant;version="6.3.1",
+ org.eclipse.jgit.ant.tasks;version="6.3.1";
   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 4e3785e..af7a021 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: 6.2.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.ant;version="6.2.1.qualifier";roots="."
+Bundle-Version: 6.3.1.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.ant;version="6.3.1.qualifier";roots="."
diff --git a/org.eclipse.jgit.ant/pom.xml b/org.eclipse.jgit.ant/pom.xml
index c16f983..df207ca 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>6.2.1-SNAPSHOT</version>
+    <version>6.3.1-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 cd4b762..42e207d 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: 6.2.1.qualifier
+Bundle-Version: 6.3.1.qualifier
 Bundle-Vendor: %Bundle-Vendor
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-11
@@ -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="[6.2.1,6.3.0)",
- org.eclipse.jgit.lib;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.nls;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.revwalk;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.util;version="[6.2.1,6.3.0)",
+ org.eclipse.jgit.api;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.lib;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.nls;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.revwalk;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.util;version="[6.3.1,6.4.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="6.2.1";
+Export-Package: org.eclipse.jgit.archive;version="6.3.1";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.api,
    org.apache.commons.compress.archivers,
    org.osgi.framework",
- org.eclipse.jgit.archive.internal;version="6.2.1";x-internal:=true
+ org.eclipse.jgit.archive.internal;version="6.3.1";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 000fce5..816fef3 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: 6.2.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.archive;version="6.2.1.qualifier";roots="."
+Bundle-Version: 6.3.1.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.archive;version="6.3.1.qualifier";roots="."
diff --git a/org.eclipse.jgit.archive/pom.xml b/org.eclipse.jgit.archive/pom.xml
index 3e3b6d2..3376350 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>6.2.1-SNAPSHOT</version>
+    <version>6.3.1-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 78e19e9..dfab1d9 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>6.2.1-SNAPSHOT</version>
+  <version>6.3.1-SNAPSHOT</version>
   <artifactId>org.eclipse.jgit.benchmarks</artifactId>
   <packaging>jar</packaging>
 
diff --git a/org.eclipse.jgit.coverage/pom.xml b/org.eclipse.jgit.coverage/pom.xml
index 70370a4..d148b72 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>6.2.1-SNAPSHOT</version>
+    <version>6.3.1-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
 
@@ -27,88 +27,88 @@
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit</artifactId>
-      <version>6.2.1-SNAPSHOT</version>
+      <version>6.3.1-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.ant</artifactId>
-      <version>6.2.1-SNAPSHOT</version>
+      <version>6.3.1-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.archive</artifactId>
-      <version>6.2.1-SNAPSHOT</version>
+      <version>6.3.1-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.http.apache</artifactId>
-      <version>6.2.1-SNAPSHOT</version>
+      <version>6.3.1-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.http.server</artifactId>
-      <version>6.2.1-SNAPSHOT</version>
+      <version>6.3.1-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.lfs</artifactId>
-      <version>6.2.1-SNAPSHOT</version>
+      <version>6.3.1-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.lfs.server</artifactId>
-      <version>6.2.1-SNAPSHOT</version>
+      <version>6.3.1-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.pgm</artifactId>
-      <version>6.2.1-SNAPSHOT</version>
+      <version>6.3.1-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.ui</artifactId>
-      <version>6.2.1-SNAPSHOT</version>
+      <version>6.3.1-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.ssh.apache</artifactId>
-      <version>6.2.1-SNAPSHOT</version>
+      <version>6.3.1-SNAPSHOT</version>
     </dependency>
 
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.test</artifactId>
-      <version>6.2.1-SNAPSHOT</version>
+      <version>6.3.1-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.ant.test</artifactId>
-      <version>6.2.1-SNAPSHOT</version>
+      <version>6.3.1-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.http.test</artifactId>
-      <version>6.2.1-SNAPSHOT</version>
+      <version>6.3.1-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.pgm.test</artifactId>
-      <version>6.2.1-SNAPSHOT</version>
+      <version>6.3.1-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.lfs.test</artifactId>
-      <version>6.2.1-SNAPSHOT</version>
+      <version>6.3.1-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.lfs.server.test</artifactId>
-      <version>6.2.1-SNAPSHOT</version>
+      <version>6.3.1-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.ssh.apache.test</artifactId>
-      <version>6.2.1-SNAPSHOT</version>
+      <version>6.3.1-SNAPSHOT</version>
     </dependency>
   </dependencies>
 
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 383de17..0eec0f6 100644
--- a/org.eclipse.jgit.gpg.bc.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.gpg.bc.test/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@
 Bundle-Name: %Bundle-Name
 Automatic-Module-Name: org.eclipse.jgit.gpg.bc.test
 Bundle-SymbolicName: org.eclipse.jgit.gpg.bc.test
-Bundle-Version: 6.2.1.qualifier
+Bundle-Version: 6.3.1.qualifier
 Bundle-Vendor: %Bundle-Vendor
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-11
@@ -12,9 +12,9 @@
  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.gpg.bc.internal;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.gpg.bc.internal.keys;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.util.sha1;version="[6.2.1,6.3.0)",
+ org.eclipse.jgit.gpg.bc.internal;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.gpg.bc.internal.keys;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.util.sha1;version="[6.3.1,6.4.0)",
  org.hamcrest;version="[1.1.0,3.0.0)",
  org.junit;version="[4.13,5.0.0)",
  org.junit.runner;version="[4.13,5.0.0)",
diff --git a/org.eclipse.jgit.gpg.bc.test/pom.xml b/org.eclipse.jgit.gpg.bc.test/pom.xml
index 653c832..5f16956 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>6.2.1-SNAPSHOT</version>
+    <version>6.3.1-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 454195d..02d9cfe 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="[6.2.1,6.3.0)"
+Fragment-Host: org.eclipse.jgit;bundle-version="[6.3.1,6.4.0)"
 Bundle-Vendor: %Bundle-Vendor
 Bundle-Localization: plugin
-Bundle-Version: 6.2.1.qualifier
+Bundle-Version: 6.3.1.qualifier
 Bundle-RequiredExecutionEnvironment: JavaSE-11
 Import-Package: org.bouncycastle.asn1;version="[1.69.0,2.0.0)",
  org.bouncycastle.asn1.cryptlib;version="[1.69.0,2.0.0)",
@@ -29,9 +29,9 @@
  org.bouncycastle.util;version="[1.69.0,2.0.0)",
  org.bouncycastle.util.encoders;version="[1.69.0,2.0.0)",
  org.bouncycastle.util.io;version="[1.69.0,2.0.0)",
- org.eclipse.jgit.annotations;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.api.errors;version="[6.2.1,6.3.0)",
+ org.eclipse.jgit.annotations;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.api.errors;version="[6.3.1,6.4.0)",
  org.slf4j;version="[1.7.0,2.0.0)"
-Export-Package: org.eclipse.jgit.gpg.bc;version="6.2.1",
- org.eclipse.jgit.gpg.bc.internal;version="6.2.1";x-friends:="org.eclipse.jgit.gpg.bc.test",
- org.eclipse.jgit.gpg.bc.internal.keys;version="6.2.1";x-friends:="org.eclipse.jgit.gpg.bc.test"
+Export-Package: org.eclipse.jgit.gpg.bc;version="6.3.1",
+ org.eclipse.jgit.gpg.bc.internal;version="6.3.1";x-friends:="org.eclipse.jgit.gpg.bc.test",
+ org.eclipse.jgit.gpg.bc.internal.keys;version="6.3.1";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 2d71a5e..795cbce 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: 6.2.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.gpg.bc;version="6.2.1.qualifier";roots="."
+Bundle-Version: 6.3.1.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.gpg.bc;version="6.3.1.qualifier";roots="."
diff --git a/org.eclipse.jgit.gpg.bc/pom.xml b/org.eclipse.jgit.gpg.bc/pom.xml
index 8df5909..3110807 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>6.2.1-SNAPSHOT</version>
+    <version>6.3.1-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.gpg.bc</artifactId>
@@ -41,22 +41,22 @@
 
     <dependency>
       <groupId>org.bouncycastle</groupId>
-      <artifactId>bcpg-jdk15on</artifactId>
+      <artifactId>bcpg-jdk18on</artifactId>
     </dependency>
 
     <dependency>
       <groupId>org.bouncycastle</groupId>
-      <artifactId>bcprov-jdk15on</artifactId>
+      <artifactId>bcprov-jdk18on</artifactId>
     </dependency>
 
     <dependency>
       <groupId>org.bouncycastle</groupId>
-      <artifactId>bcutil-jdk15on</artifactId>
+      <artifactId>bcutil-jdk18on</artifactId>
     </dependency>
 
     <dependency>
       <groupId>org.bouncycastle</groupId>
-      <artifactId>bcpkix-jdk15on</artifactId>
+      <artifactId>bcpkix-jdk18on</artifactId>
     </dependency>
 
     <dependency>
diff --git a/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF
index bbde3f1..0587abf 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: 6.2.1.qualifier
+Bundle-Version: 6.3.1.qualifier
 Bundle-RequiredExecutionEnvironment: JavaSE-11
 Bundle-Localization: plugin
 Bundle-Vendor: %Bundle-Vendor
@@ -25,11 +25,11 @@
  org.apache.http.impl.conn;version="[4.4.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="[6.2.1,6.3.0)",
- org.eclipse.jgit.nls;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.transport.http;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.util;version="[6.2.1,6.3.0)"
-Export-Package: org.eclipse.jgit.transport.http.apache;version="6.2.1";
+ org.eclipse.jgit.annotations;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.nls;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.transport.http;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.util;version="[6.3.1,6.4.0)"
+Export-Package: org.eclipse.jgit.transport.http.apache;version="6.3.1";
   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 fe928fb..fcc2df7 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: 6.2.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.http.apache;version="6.2.1.qualifier";roots="."
+Bundle-Version: 6.3.1.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.http.apache;version="6.3.1.qualifier";roots="."
diff --git a/org.eclipse.jgit.http.apache/pom.xml b/org.eclipse.jgit.http.apache/pom.xml
index 6182159..a7e4f3b 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>6.2.1-SNAPSHOT</version>
+    <version>6.3.1-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 2bdf386..2eb2978 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: 6.2.1.qualifier
+Bundle-Version: 6.3.1.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %Bundle-Vendor
-Export-Package: org.eclipse.jgit.http.server;version="6.2.1",
- org.eclipse.jgit.http.server.glue;version="6.2.1";
+Export-Package: org.eclipse.jgit.http.server;version="6.3.1",
+ org.eclipse.jgit.http.server.glue;version="6.3.1";
   uses:="javax.servlet,javax.servlet.http",
- org.eclipse.jgit.http.server.resolver;version="6.2.1";
+ org.eclipse.jgit.http.server.resolver;version="6.3.1";
   uses:="org.eclipse.jgit.transport.resolver,
    org.eclipse.jgit.lib,
    org.eclipse.jgit.transport,
@@ -18,14 +18,14 @@
 Bundle-RequiredExecutionEnvironment: JavaSE-11
 Import-Package: javax.servlet;version="[2.5.0,5.0.0)",
  javax.servlet.http;version="[2.5.0,5.0.0)",
- org.eclipse.jgit.annotations;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.errors;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.internal.storage.dfs;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.internal.storage.file;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.internal.transport.parser;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.lib;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.nls;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.revwalk;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.transport;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.transport.resolver;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.util;version="[6.2.1,6.3.0)"
+ org.eclipse.jgit.annotations;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.errors;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.internal.transport.parser;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.lib;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.nls;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.revwalk;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.transport;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.transport.resolver;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.util;version="[6.3.1,6.4.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 bdcba51..b9aefb0 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: 6.2.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.http.server;version="6.2.1.qualifier";roots="."
+Bundle-Version: 6.3.1.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.http.server;version="6.3.1.qualifier";roots="."
diff --git a/org.eclipse.jgit.http.server/pom.xml b/org.eclipse.jgit.http.server/pom.xml
index 156d9f2..8364fc6 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>6.2.1-SNAPSHOT</version>
+    <version>6.3.1-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.http.server</artifactId>
diff --git a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF
index 04fd363..d585f28 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: 6.2.1.qualifier
+Bundle-Version: 6.3.1.qualifier
 Bundle-Vendor: %Bundle-Vendor
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-11
@@ -26,26 +26,26 @@
  org.eclipse.jetty.util.log;version="[10.0.0,11.0.0)",
  org.eclipse.jetty.util.security;version="[10.0.0,11.0.0)",
  org.eclipse.jetty.util.thread;version="[10.0.0,11.0.0)",
- org.eclipse.jgit.api;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.errors;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.http.server;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.http.server.glue;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.http.server.resolver;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.internal;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.internal.storage.dfs;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.internal.storage.file;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.internal.storage.reftable;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.junit;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.junit.http;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.lib;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.nls;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.revwalk;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.storage.file;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.transport;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.transport.http;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.transport.http.apache;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.transport.resolver;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.util;version="[6.2.1,6.3.0)",
+ org.eclipse.jgit.api;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.errors;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.http.server;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.http.server.glue;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.http.server.resolver;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.internal;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.internal.storage.reftable;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.junit;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.junit.http;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.lib;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.nls;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.revwalk;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.storage.file;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.transport;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.transport.http;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.transport.http.apache;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.transport.resolver;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.util;version="[6.3.1,6.4.0)",
  org.hamcrest;version="[1.1.0,3.0.0)",
  org.hamcrest.core;version="[1.1.0,3.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 3d6aa3d..f71782b 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>6.2.1-SNAPSHOT</version>
+    <version>6.3.1-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.http.test</artifactId>
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java
index 3438c52..6b06727 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java
@@ -24,13 +24,16 @@
 import java.net.URI;
 import java.net.URL;
 import java.text.MessageFormat;
+import java.time.Instant;
 import java.util.List;
+import java.util.Set;
 
 import javax.servlet.http.HttpServletRequest;
 
 import org.eclipse.jetty.servlet.DefaultServlet;
 import org.eclipse.jetty.servlet.ServletContextHandler;
 import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jgit.api.Git;
 import org.eclipse.jgit.errors.NoRemoteRepositoryException;
 import org.eclipse.jgit.errors.RepositoryNotFoundException;
 import org.eclipse.jgit.errors.TransportException;
@@ -408,4 +411,110 @@ public void testV2HttpSubsequentResponse() throws Exception {
 
 		assertEquals(200, c.getResponseCode());
 	}
+
+	@Test
+	public void testCloneWithDepth() throws Exception {
+		remoteRepository.getRepository().getConfig().setInt(
+				"protocol", null, "version", 0);
+		File directory = createTempDirectory("testCloneWithDepth");
+		Git git = Git.cloneRepository()
+					 .setDirectory(directory)
+					 .setDepth(1)
+					 .setURI(smartAuthNoneURI.toString())
+					 .call();
+
+		assertEquals(Set.of(git.getRepository().resolve(Constants.HEAD)), git.getRepository().getObjectDatabase().getShallowCommits());
+	}
+
+	@Test
+	public void testCloneWithDeepenSince() throws Exception {
+		remoteRepository.getRepository().getConfig().setInt(
+				"protocol", null, "version", 0);
+		RevCommit commit = remoteRepository.commit()
+										   .parent(remoteRepository.git().log().call().iterator().next())
+										   .message("Test")
+										   .add("test.txt", "Hello world")
+										   .create();
+		remoteRepository.update(master, commit);
+
+		File directory = createTempDirectory("testCloneWithDeepenSince");
+		Git git = Git.cloneRepository()
+					 .setDirectory(directory)
+					 .setShallowSince(Instant.ofEpochSecond(commit.getCommitTime()))
+					 .setURI(smartAuthNoneURI.toString())
+					 .call();
+
+		assertEquals(Set.of(git.getRepository().resolve(Constants.HEAD)), git.getRepository().getObjectDatabase().getShallowCommits());
+	}
+
+	@Test
+	public void testCloneWithDeepenNot() throws Exception {
+		remoteRepository.getRepository().getConfig().setInt(
+				"protocol", null, "version", 0);
+		RevCommit commit = remoteRepository.git().log().call().iterator().next();
+		remoteRepository.update(master, remoteRepository.commit()
+														.parent(commit)
+														.message("Test")
+														.add("test.txt", "Hello world")
+														.create());
+
+		File directory = createTempDirectory("testCloneWithDeepenNot");
+		Git git = Git.cloneRepository()
+					 .setDirectory(directory)
+					 .addShallowExclude(commit.getId())
+					 .setURI(smartAuthNoneURI.toString())
+					 .call();
+
+		assertEquals(Set.of(git.getRepository().resolve(Constants.HEAD)), git.getRepository().getObjectDatabase().getShallowCommits());
+	}
+
+    @Test
+	public void testV2CloneWithDepth() throws Exception {
+        File directory = createTempDirectory("testV2CloneWithDepth");
+        Git git = Git.cloneRepository()
+                     .setDirectory(directory)
+                     .setDepth(1)
+                     .setURI(smartAuthNoneURI.toString())
+                     .call();
+
+        assertEquals(Set.of(git.getRepository().resolve(Constants.HEAD)), git.getRepository().getObjectDatabase().getShallowCommits());
+    }
+
+    @Test
+    public void testV2CloneWithDeepenSince() throws Exception {
+        RevCommit commit = remoteRepository.commit()
+                                           .parent(remoteRepository.git().log().call().iterator().next())
+                                           .message("Test")
+                                           .add("test.txt", "Hello world")
+                                           .create();
+        remoteRepository.update(master, commit);
+
+        File directory = createTempDirectory("testV2CloneWithDeepenSince");
+        Git git = Git.cloneRepository()
+                     .setDirectory(directory)
+                     .setShallowSince(Instant.ofEpochSecond(commit.getCommitTime()))
+                     .setURI(smartAuthNoneURI.toString())
+                     .call();
+
+		assertEquals(Set.of(git.getRepository().resolve(Constants.HEAD)), git.getRepository().getObjectDatabase().getShallowCommits());
+    }
+
+    @Test
+    public void testV2CloneWithDeepenNot() throws Exception {
+        RevCommit commit = remoteRepository.git().log().call().iterator().next();
+        remoteRepository.update(master, remoteRepository.commit()
+                                                        .parent(commit)
+                                                        .message("Test")
+                                                        .add("test.txt", "Hello world")
+                                                        .create());
+
+        File directory = createTempDirectory("testV2CloneWithDeepenNot");
+        Git git = Git.cloneRepository()
+                     .setDirectory(directory)
+                     .addShallowExclude(commit.getId())
+                     .setURI(smartAuthNoneURI.toString())
+                     .call();
+
+		assertEquals(Set.of(git.getRepository().resolve(Constants.HEAD)), git.getRepository().getObjectDatabase().getShallowCommits());
+    }
 }
diff --git a/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF
index 3a821d5..30e3306 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: 6.2.1.qualifier
+Bundle-Version: 6.3.1.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %Bundle-Vendor
 Bundle-ActivationPolicy: lazy
@@ -21,17 +21,17 @@
  org.eclipse.jetty.util.log;version="[10.0.0,11.0.0)",
  org.eclipse.jetty.util.security;version="[10.0.0,11.0.0)",
  org.eclipse.jetty.util.ssl;version="[10.0.0,11.0.0)",
- org.eclipse.jgit.errors;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.http.server;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.internal.storage.file;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.junit;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.lib;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.revwalk;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.transport;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.transport.resolver;version="[6.2.1,6.3.0)",
+ org.eclipse.jgit.errors;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.http.server;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.junit;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.lib;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.revwalk;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.transport;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.transport.resolver;version="[6.3.1,6.4.0)",
  org.junit;version="[4.13,5.0.0)",
  org.slf4j.helpers;version="[1.7.0,2.0.0)"
-Export-Package: org.eclipse.jgit.junit.http;version="6.2.1";
+Export-Package: org.eclipse.jgit.junit.http;version="6.3.1";
   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 036ca8a..d50a9a4 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: 6.2.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.junit.http;version="6.2.1.qualifier";roots="."
+Bundle-Version: 6.3.1.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.junit.http;version="6.3.1.qualifier";roots="."
diff --git a/org.eclipse.jgit.junit.http/pom.xml b/org.eclipse.jgit.junit.http/pom.xml
index 7a3db92..7020d02 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>6.2.1-SNAPSHOT</version>
+    <version>6.3.1-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.junit.http</artifactId>
diff --git a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AccessEvent.java b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AccessEvent.java
index 22e1997..873d430 100644
--- a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AccessEvent.java
+++ b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AccessEvent.java
@@ -30,16 +30,18 @@ public class AccessEvent {
 
 	private final Map<String, String[]> parameters;
 
-	private final int status;
+	private int status;
 
-	private final Map<String, String> responseHeaders;
+	private Map<String, String> responseHeaders;
 
-	AccessEvent(Request req, Response rsp) {
+	AccessEvent(Request req) {
 		method = req.getMethod();
 		uri = req.getRequestURI();
 		requestHeaders = cloneHeaders(req);
 		parameters = clone(req.getParameterMap());
+	}
 
+	void setResponse(Response rsp) {
 		status = rsp.getStatus();
 		responseHeaders = cloneHeaders(rsp);
 	}
@@ -141,7 +143,7 @@ public int getStatus() {
 	 * @return first value of the response header; null if not sent.
 	 */
 	public String getResponseHeader(String name) {
-		return responseHeaders.get(name);
+		return responseHeaders != null ? responseHeaders.get(name) : null;
 	}
 
 	/** {@inheritDoc} */
diff --git a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/TestRequestLog.java b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/TestRequestLog.java
index a86edd2..04cb242 100644
--- a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/TestRequestLog.java
+++ b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/TestRequestLog.java
@@ -87,19 +87,23 @@ public void handle(String target, Request baseRequest,
 				}
 			}
 
+			AccessEvent event = null;
+			if (DispatcherType.REQUEST
+					.equals(baseRequest.getDispatcherType())) {
+				event = new AccessEvent((Request) request);
+				synchronized (events) {
+					events.add(event);
+				}
+			}
+
 			super.handle(target, baseRequest, request, response);
 
-			if (DispatcherType.REQUEST.equals(baseRequest.getDispatcherType()))
-				log((Request) request, (Response) response);
+			if (event != null) {
+				event.setResponse((Response) response);
+			}
 
 		} finally {
 			active.release();
 		}
 	}
-
-	private void log(Request request, Response response) {
-		synchronized (events) {
-			events.add(new AccessEvent(request, response));
-		}
-	}
 }
diff --git a/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF
index 278ed7e..e878090 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: 6.2.1.qualifier
+Bundle-Version: 6.3.1.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %Bundle-Vendor
 Bundle-ActivationPolicy: lazy
@@ -33,16 +33,16 @@
  org.apache.sshd.server.subsystem;version="[2.8.0,2.9.0)",
  org.apache.sshd.sftp;version="[2.8.0,2.9.0)",
  org.apache.sshd.sftp.server;version="[2.8.0,2.9.0)",
- org.eclipse.jgit.annotations;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.api;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.api.errors;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.errors;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.junit;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.lib;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.revwalk;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.transport;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.util;version="[6.2.1,6.3.0)",
+ org.eclipse.jgit.annotations;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.api;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.api.errors;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.errors;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.junit;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.lib;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.revwalk;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.transport;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.util;version="[6.3.1,6.4.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="6.2.1"
+Export-Package: org.eclipse.jgit.junit.ssh;version="6.3.1"
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 f77aafd..8627975 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: 6.2.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.junit.ssh;version="6.2.1.qualifier";roots="."
+Bundle-Version: 6.3.1.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.junit.ssh;version="6.3.1.qualifier";roots="."
diff --git a/org.eclipse.jgit.junit.ssh/pom.xml b/org.eclipse.jgit.junit.ssh/pom.xml
index cf1aaf8..ba82ed4 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>6.2.1-SNAPSHOT</version>
+    <version>6.3.1-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.junit.ssh</artifactId>
diff --git a/org.eclipse.jgit.junit/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit/META-INF/MANIFEST.MF
index a3a2f34..b2cb44b 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: 6.2.1.qualifier
+Bundle-Version: 6.3.1.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %Bundle-Vendor
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: JavaSE-11
-Import-Package: org.eclipse.jgit.annotations;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.api;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.api.errors;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.dircache;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.errors;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.internal.storage.file;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.internal.storage.pack;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.lib;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.merge;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.revwalk;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.storage.file;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.transport;version="6.2.1",
- org.eclipse.jgit.treewalk;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.treewalk.filter;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.util;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.util.io;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.util.time;version="[6.2.1,6.3.0)",
+Import-Package: org.eclipse.jgit.annotations;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.api;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.api.errors;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.dircache;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.errors;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.lib;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.merge;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.revwalk;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.storage.file;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.transport;version="6.3.1",
+ org.eclipse.jgit.treewalk;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.treewalk.filter;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.util;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.util.io;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.util.time;version="[6.3.1,6.4.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="6.2.1";
+Export-Package: org.eclipse.jgit.junit;version="6.3.1";
   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="6.2.1";uses:="org.eclipse.jgit.util.time"
+ org.eclipse.jgit.junit.time;version="6.3.1";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 6997a51..4daa576 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: 6.2.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.junit;version="6.2.1.qualifier";roots="."
+Bundle-Version: 6.3.1.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.junit;version="6.3.1.qualifier";roots="."
diff --git a/org.eclipse.jgit.junit/pom.xml b/org.eclipse.jgit.junit/pom.xml
index a7e0831..fe2211b 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>6.2.1-SNAPSHOT</version>
+    <version>6.3.1-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.junit</artifactId>
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java
index 54e4a09..483b9a7 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java
@@ -276,6 +276,24 @@ public DirCacheEntry file(String path, RevBlob blob)
 	}
 
 	/**
+	 * Construct a symlink mode tree entry.
+	 *
+	 * @param path
+	 *            path of the symlink.
+	 * @param blob
+	 *            a blob, previously constructed in the repository.
+	 * @return the entry.
+	 * @throws Exception
+	 * @since 6.3
+	 */
+	public DirCacheEntry link(String path, RevBlob blob) throws Exception {
+		DirCacheEntry e = new DirCacheEntry(path);
+		e.setFileMode(FileMode.SYMLINK);
+		e.setObjectId(blob);
+		return e;
+	}
+
+	/**
 	 * Construct a tree from a specific listing of file entries.
 	 *
 	 * @param entries
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 6a9f172..5962ebb 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: 6.2.1.qualifier
+Bundle-Version: 6.3.1.qualifier
 Bundle-Vendor: %Bundle-Vendor
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-11
@@ -26,24 +26,24 @@
  org.eclipse.jetty.util.log;version="[10.0.0,11.0.0)",
  org.eclipse.jetty.util.security;version="[10.0.0,11.0.0)",
  org.eclipse.jetty.util.thread;version="[10.0.0,11.0.0)",
- org.eclipse.jgit.api;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.api.errors;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.internal.storage.file;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.junit;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.junit.http;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.lfs;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.lfs.errors;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.lfs.lib;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.lfs.server;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.lfs.server.fs;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.lfs.test;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.lib;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.revwalk;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.storage.file;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.transport;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.treewalk;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.treewalk.filter;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.util;version="[6.2.1,6.3.0)",
+ org.eclipse.jgit.api;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.api.errors;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.junit;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.junit.http;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.lfs;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.lfs.errors;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.lfs.lib;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.lfs.server;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.lfs.server.fs;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.lfs.test;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.lib;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.revwalk;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.storage.file;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.transport;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.treewalk;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.treewalk.filter;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.util;version="[6.3.1,6.4.0)",
  org.hamcrest.core;version="[1.1.0,3.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 e306c3a..baa6a9d 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>6.2.1-SNAPSHOT</version>
+    <version>6.3.1-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 c63d2e4..1e7dc5f 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: 6.2.1.qualifier
+Bundle-Version: 6.3.1.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %Bundle-Vendor
-Export-Package: org.eclipse.jgit.lfs.server;version="6.2.1";
+Export-Package: org.eclipse.jgit.lfs.server;version="6.3.1";
   uses:="javax.servlet.http,
    org.eclipse.jgit.lfs.lib",
- org.eclipse.jgit.lfs.server.fs;version="6.2.1";
+ org.eclipse.jgit.lfs.server.fs;version="6.3.1";
   uses:="javax.servlet,
    javax.servlet.http,
    org.eclipse.jgit.lfs.server,
    org.eclipse.jgit.lfs.lib",
- org.eclipse.jgit.lfs.server.internal;version="6.2.1";x-internal:=true,
- org.eclipse.jgit.lfs.server.s3;version="6.2.1";
+ org.eclipse.jgit.lfs.server.internal;version="6.3.1";x-internal:=true,
+ org.eclipse.jgit.lfs.server.s3;version="6.3.1";
   uses:="org.eclipse.jgit.lfs.server,
    org.eclipse.jgit.lfs.lib"
 Bundle-RequiredExecutionEnvironment: JavaSE-11
@@ -24,15 +24,15 @@
  javax.servlet.annotation;version="[3.1.0,5.0.0)",
  javax.servlet.http;version="[3.1.0,5.0.0)",
  org.apache.http;version="[4.3.0,5.0.0)",
- org.eclipse.jgit.annotations;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.internal;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.internal.storage.file;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.lfs.errors;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.lfs.internal;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.lfs.lib;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.lib;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.nls;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.transport.http;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.transport.http.apache;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.util;version="[6.2.1,6.3.0)",
+ org.eclipse.jgit.annotations;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.internal;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.lfs.errors;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.lfs.internal;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.lfs.lib;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.lib;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.nls;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.transport.http;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.transport.http.apache;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.util;version="[6.3.1,6.4.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 3b783ca..b42fb0a 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: 6.2.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.lfs.server;version="6.2.1.qualifier";roots="."
+Bundle-Version: 6.3.1.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.lfs.server;version="6.3.1.qualifier";roots="."
diff --git a/org.eclipse.jgit.lfs.server/pom.xml b/org.eclipse.jgit.lfs.server/pom.xml
index 0e5deb7..7944938 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>6.2.1-SNAPSHOT</version>
+    <version>6.3.1-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.lfs.server</artifactId>
diff --git a/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF
index 80b8514..e849423 100644
--- a/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF
@@ -3,27 +3,27 @@
 Bundle-Name: %Bundle-Name
 Automatic-Module-Name: org.eclipse.jgit.lfs.test
 Bundle-SymbolicName: org.eclipse.jgit.lfs.test
-Bundle-Version: 6.2.1.qualifier
+Bundle-Version: 6.3.1.qualifier
 Bundle-Vendor: %Bundle-Vendor
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-11
-Import-Package: org.eclipse.jgit.api;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.attributes;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.internal.storage.dfs;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.junit;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.lfs;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.lfs.errors;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.lfs.internal;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.lfs.lib;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.lib;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.revwalk;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.transport;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.transport.http;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.treewalk;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.treewalk.filter;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.util;version="[6.2.1,6.3.0)",
+Import-Package: org.eclipse.jgit.api;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.attributes;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.junit;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.lfs;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.lfs.errors;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.lfs.internal;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.lfs.lib;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.lib;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.revwalk;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.transport;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.transport.http;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.treewalk;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.treewalk.filter;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.util;version="[6.3.1,6.4.0)",
  org.hamcrest.core;version="[1.1.0,3.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="6.2.1";x-friends:="org.eclipse.jgit.lfs.server.test"
+Export-Package: org.eclipse.jgit.lfs.test;version="6.3.1";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 357ada3..ab82454 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>6.2.1-SNAPSHOT</version>
+    <version>6.3.1-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 bc4f4f4..f1aee3a 100644
--- a/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF
@@ -3,32 +3,32 @@
 Bundle-Name: %Bundle-Name
 Automatic-Module-Name: org.eclipse.jgit.lfs
 Bundle-SymbolicName: org.eclipse.jgit.lfs
-Bundle-Version: 6.2.1.qualifier
+Bundle-Version: 6.3.1.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %Bundle-Vendor
-Export-Package: org.eclipse.jgit.lfs;version="6.2.1",
- org.eclipse.jgit.lfs.errors;version="6.2.1",
- org.eclipse.jgit.lfs.internal;version="6.2.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="6.2.1"
+Export-Package: org.eclipse.jgit.lfs;version="6.3.1",
+ org.eclipse.jgit.lfs.errors;version="6.3.1",
+ org.eclipse.jgit.lfs.internal;version="6.3.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="6.3.1"
 Bundle-RequiredExecutionEnvironment: JavaSE-11
 Import-Package: com.google.gson;version="[2.8.2,3.0.0)",
  com.google.gson.stream;version="[2.8.2,3.0.0)",
- org.eclipse.jgit.annotations;version="[6.2.1,6.3.0)";resolution:=optional,
- org.eclipse.jgit.api.errors;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.attributes;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.diff;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.dircache;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.errors;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.hooks;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.internal.storage.file;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.lib;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.nls;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.revwalk;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.storage.file;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.storage.pack;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.transport;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.transport.http;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.treewalk;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.treewalk.filter;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.util;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.util.io;version="[6.2.1,6.3.0)"
+ org.eclipse.jgit.annotations;version="[6.3.1,6.4.0)";resolution:=optional,
+ org.eclipse.jgit.api.errors;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.attributes;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.diff;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.dircache;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.errors;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.hooks;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.lib;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.nls;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.revwalk;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.storage.file;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.storage.pack;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.transport;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.transport.http;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.treewalk;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.treewalk.filter;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.util;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.util.io;version="[6.3.1,6.4.0)"
diff --git a/org.eclipse.jgit.lfs/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.lfs/META-INF/SOURCE-MANIFEST.MF
index 5faa06f..e1ff6fa 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: 6.2.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.lfs;version="6.2.1.qualifier";roots="."
+Bundle-Version: 6.3.1.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.lfs;version="6.3.1.qualifier";roots="."
diff --git a/org.eclipse.jgit.lfs/pom.xml b/org.eclipse.jgit.lfs/pom.xml
index b458779..abf2ff7 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>6.2.1-SNAPSHOT</version>
+    <version>6.3.1-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 c886145..75f416a 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="6.2.1.qualifier"
+      version="6.3.1.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml
index 59c5eb9..ee11048 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>6.2.1-SNAPSHOT</version>
+    <version>6.3.1-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 b38c59a..e73ae31 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="6.2.1.qualifier"
+      version="6.3.1.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
@@ -23,7 +23,7 @@
    </url>
 
    <requires>
-      <import plugin="org.eclipse.jgit" version="6.2.1" match="equivalent"/>
+      <import plugin="org.eclipse.jgit" version="6.3.1" match="equivalent"/>
    </requires>
 
    <plugin
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 eaa0916..1bbf567 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>6.2.1-SNAPSHOT</version>
+    <version>6.3.1-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 01b978a..58d484f 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="6.2.1.qualifier"
+      version="6.3.1.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
@@ -23,7 +23,7 @@
    </url>
 
    <requires>
-      <import plugin="org.eclipse.jgit" version="6.2.1" match="equivalent"/>
+      <import plugin="org.eclipse.jgit" version="6.3.1" match="equivalent"/>
    </requires>
 
    <plugin
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml
index f6f137c..00ff3ce 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>6.2.1-SNAPSHOT</version>
+    <version>6.3.1-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 4bf27d4..9e2aec3 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="6.2.1.qualifier"
+      version="6.3.1.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="6.2.1" match="equivalent"/>
+      <import plugin="org.eclipse.jgit" version="6.3.1" 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 d81b081..ecfcf55 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>6.2.1-SNAPSHOT</version>
+    <version>6.3.1-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 4e8385b..0996158 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="6.2.1.qualifier"
+      version="6.3.1.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
@@ -23,7 +23,7 @@
    </url>
 
    <requires>
-      <import feature="org.eclipse.jgit" version="6.2.1" match="equivalent"/>
+      <import feature="org.eclipse.jgit" version="6.3.1" match="equivalent"/>
    </requires>
 
    <plugin
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/pom.xml
index 7b2b512..6c415aa 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>6.2.1-SNAPSHOT</version>
+    <version>6.3.1-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 38766d2..9aa6af1 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="6.2.1.qualifier"
+      version="6.3.1.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="6.2.1" match="equivalent"/>
-      <import feature="org.eclipse.jgit.lfs" version="6.2.1" match="equivalent"/>
-      <import feature="org.eclipse.jgit.ssh.apache" version="6.2.1" match="equivalent"/>
+      <import feature="org.eclipse.jgit" version="6.3.1" match="equivalent"/>
+      <import feature="org.eclipse.jgit.lfs" version="6.3.1" match="equivalent"/>
+      <import feature="org.eclipse.jgit.ssh.apache" version="6.3.1" match="equivalent"/>
    </requires>
 
    <plugin
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml
index f57a3c0..47a6a20 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>6.2.1-SNAPSHOT</version>
+    <version>6.3.1-SNAPSHOT</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml
index 8e8d686..9ed0f5e 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>6.2.1-SNAPSHOT</version>
+    <version>6.3.1-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 03fc43a..fb5b764 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="6.2.1.qualifier"
+      version="6.3.1.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
@@ -23,7 +23,7 @@
    </url>
 
    <requires>
-      <import feature="org.eclipse.jgit" version="6.2.1" match="equivalent"/>
+      <import feature="org.eclipse.jgit" version="6.3.1" 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 591d67a..e558c89 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>6.2.1-SNAPSHOT</version>
+    <version>6.3.1-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>6.2.1-SNAPSHOT</version>
+      <version>6.3.1-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 40d5327..3cecf6d 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="6.2.1.qualifier"
+      version="6.3.1.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
@@ -23,7 +23,7 @@
    </url>
 
    <requires>
-      <import feature="org.eclipse.jgit" version="6.2.1" match="equivalent"/>
+      <import feature="org.eclipse.jgit" version="6.3.1" match="equivalent"/>
    </requires>
 
    <plugin
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 8497f08..92a0300 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>6.2.1-SNAPSHOT</version>
+    <version>6.3.1-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 0018abe..a8bae00 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="6.2.1.qualifier"
+      version="6.3.1.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
@@ -23,7 +23,7 @@
    </url>
 
    <requires>
-      <import plugin="org.eclipse.jgit" version="6.2.1" match="equivalent"/>
+      <import plugin="org.eclipse.jgit" version="6.3.1" match="equivalent"/>
    </requires>
 
    <plugin
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 6ee46ba..bf08c98 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>6.2.1-SNAPSHOT</version>
+    <version>6.3.1-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 91c5228..8861f40 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: 6.2.1.qualifier
+Bundle-Version: 6.3.1.qualifier
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.target
index 3d4f237..76aafcc 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.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" sequenceNumber="1654550635">
+<target name="jgit-4.17" sequenceNumber="1662389260">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="jakarta.servlet-api" version="4.0.0"/>
@@ -59,14 +59,14 @@
       <unit id="org.apache.sshd.sftp.source" version="2.8.0.v20211227-1750"/>
       <unit id="org.assertj" version="3.20.2.v20210706-1104"/>
       <unit id="org.assertj.source" version="3.20.2.v20210706-1104"/>
-      <unit id="org.bouncycastle.bcpg" version="1.70.0.v20220507-1208"/>
-      <unit id="org.bouncycastle.bcpg.source" version="1.70.0.v20220507-1208"/>
-      <unit id="org.bouncycastle.bcpkix" version="1.70.0.v20220105-1522"/>
-      <unit id="org.bouncycastle.bcpkix.source" version="1.70.0.v20220105-1522"/>
-      <unit id="org.bouncycastle.bcprov" version="1.70.0.v20220507-1208"/>
-      <unit id="org.bouncycastle.bcprov.source" version="1.70.0.v20220507-1208"/>
-      <unit id="org.bouncycastle.bcutil" version="1.70.0.v20220105-1522"/>
-      <unit id="org.bouncycastle.bcutil.source" version="1.70.0.v20220105-1522"/>
+      <unit id="org.bouncycastle.bcpg" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcpg.source" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcpkix" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcpkix.source" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcprov" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcprov.source" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcutil" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcutil.source" version="1.71.0.v20220723-1943"/>
       <unit id="org.hamcrest" version="2.2.0.v20210711-0821"/>
       <unit id="org.hamcrest.source" version="2.2.0.v20210711-0821"/>
       <unit id="org.hamcrest.core" version="1.3.0.v20180420-1519"/>
@@ -87,7 +87,7 @@
       <unit id="org.slf4j.binding.simple.source" version="1.7.30.v20200204-2150"/>
       <unit id="org.tukaani.xz" version="1.9.0.v20210624-1259"/>
       <unit id="org.tukaani.xz.source" version="1.9.0.v20210624-1259"/>
-      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20220531185310/repository"/>
+      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20220830213456/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.17.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.tpd
index 9c824c5..8c28b96 100644
--- 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
@@ -1,7 +1,7 @@
 target "jgit-4.17" with source configurePhase
 
 include "projects/jetty-10.0.x.tpd"
-include "orbit/R20220531185310-2022-06.tpd"
+include "orbit/R20220830213456-2022-09.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.18.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.target
index 603fefb..f48ed6e 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.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.18" sequenceNumber="1654550635">
+<target name="jgit-4.18" sequenceNumber="1662389260">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="jakarta.servlet-api" version="4.0.0"/>
@@ -59,14 +59,14 @@
       <unit id="org.apache.sshd.sftp.source" version="2.8.0.v20211227-1750"/>
       <unit id="org.assertj" version="3.20.2.v20210706-1104"/>
       <unit id="org.assertj.source" version="3.20.2.v20210706-1104"/>
-      <unit id="org.bouncycastle.bcpg" version="1.70.0.v20220507-1208"/>
-      <unit id="org.bouncycastle.bcpg.source" version="1.70.0.v20220507-1208"/>
-      <unit id="org.bouncycastle.bcpkix" version="1.70.0.v20220105-1522"/>
-      <unit id="org.bouncycastle.bcpkix.source" version="1.70.0.v20220105-1522"/>
-      <unit id="org.bouncycastle.bcprov" version="1.70.0.v20220507-1208"/>
-      <unit id="org.bouncycastle.bcprov.source" version="1.70.0.v20220507-1208"/>
-      <unit id="org.bouncycastle.bcutil" version="1.70.0.v20220105-1522"/>
-      <unit id="org.bouncycastle.bcutil.source" version="1.70.0.v20220105-1522"/>
+      <unit id="org.bouncycastle.bcpg" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcpg.source" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcpkix" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcpkix.source" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcprov" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcprov.source" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcutil" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcutil.source" version="1.71.0.v20220723-1943"/>
       <unit id="org.hamcrest" version="2.2.0.v20210711-0821"/>
       <unit id="org.hamcrest.source" version="2.2.0.v20210711-0821"/>
       <unit id="org.hamcrest.core" version="1.3.0.v20180420-1519"/>
@@ -87,7 +87,7 @@
       <unit id="org.slf4j.binding.simple.source" version="1.7.30.v20200204-2150"/>
       <unit id="org.tukaani.xz" version="1.9.0.v20210624-1259"/>
       <unit id="org.tukaani.xz.source" version="1.9.0.v20210624-1259"/>
-      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20220531185310/repository"/>
+      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20220830213456/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.18.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.tpd
index 1dcdd9b..a0cf7f7 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.tpd
@@ -1,7 +1,7 @@
 target "jgit-4.18" with source configurePhase
 
 include "projects/jetty-10.0.x.tpd"
-include "orbit/R20220531185310-2022-06.tpd"
+include "orbit/R20220830213456-2022-09.tpd"
 
 location "https://download.eclipse.org/releases/2020-12/" {
 	org.eclipse.osgi lazy
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.target
index cc418b1..d30352c 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.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.19-staging" sequenceNumber="1654550632">
+<target name="jgit-4.19-staging" sequenceNumber="1662389263">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="jakarta.servlet-api" version="4.0.0"/>
@@ -59,14 +59,14 @@
       <unit id="org.apache.sshd.sftp.source" version="2.8.0.v20211227-1750"/>
       <unit id="org.assertj" version="3.20.2.v20210706-1104"/>
       <unit id="org.assertj.source" version="3.20.2.v20210706-1104"/>
-      <unit id="org.bouncycastle.bcpg" version="1.70.0.v20220507-1208"/>
-      <unit id="org.bouncycastle.bcpg.source" version="1.70.0.v20220507-1208"/>
-      <unit id="org.bouncycastle.bcpkix" version="1.70.0.v20220105-1522"/>
-      <unit id="org.bouncycastle.bcpkix.source" version="1.70.0.v20220105-1522"/>
-      <unit id="org.bouncycastle.bcprov" version="1.70.0.v20220507-1208"/>
-      <unit id="org.bouncycastle.bcprov.source" version="1.70.0.v20220507-1208"/>
-      <unit id="org.bouncycastle.bcutil" version="1.70.0.v20220105-1522"/>
-      <unit id="org.bouncycastle.bcutil.source" version="1.70.0.v20220105-1522"/>
+      <unit id="org.bouncycastle.bcpg" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcpg.source" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcpkix" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcpkix.source" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcprov" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcprov.source" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcutil" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcutil.source" version="1.71.0.v20220723-1943"/>
       <unit id="org.hamcrest" version="2.2.0.v20210711-0821"/>
       <unit id="org.hamcrest.source" version="2.2.0.v20210711-0821"/>
       <unit id="org.hamcrest.core" version="1.3.0.v20180420-1519"/>
@@ -87,11 +87,11 @@
       <unit id="org.slf4j.binding.simple.source" version="1.7.30.v20200204-2150"/>
       <unit id="org.tukaani.xz" version="1.9.0.v20210624-1259"/>
       <unit id="org.tukaani.xz.source" version="1.9.0.v20210624-1259"/>
-      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20220531185310/repository"/>
+      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20220830213456/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/2021-03/"/>
+      <repository location="https://download.eclipse.org/releases/2021-03/"/>
     </location>
   </locations>
 </target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.tpd
index 806851c..ec33a08 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.tpd
@@ -1,8 +1,8 @@
 target "jgit-4.19-staging" with source configurePhase
 
 include "projects/jetty-10.0.x.tpd"
-include "orbit/R20220531185310-2022-06.tpd"
+include "orbit/R20220830213456-2022-09.tpd"
 
-location "https://download.eclipse.org/staging/2021-03/" {
+location "https://download.eclipse.org/releases/2021-03/" {
 	org.eclipse.osgi lazy
 }
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.target
index efcb591..e20ef5b 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.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.20" sequenceNumber="1654550634">
+<target name="jgit-4.20" sequenceNumber="1662389260">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="jakarta.servlet-api" version="4.0.0"/>
@@ -59,14 +59,14 @@
       <unit id="org.apache.sshd.sftp.source" version="2.8.0.v20211227-1750"/>
       <unit id="org.assertj" version="3.20.2.v20210706-1104"/>
       <unit id="org.assertj.source" version="3.20.2.v20210706-1104"/>
-      <unit id="org.bouncycastle.bcpg" version="1.70.0.v20220507-1208"/>
-      <unit id="org.bouncycastle.bcpg.source" version="1.70.0.v20220507-1208"/>
-      <unit id="org.bouncycastle.bcpkix" version="1.70.0.v20220105-1522"/>
-      <unit id="org.bouncycastle.bcpkix.source" version="1.70.0.v20220105-1522"/>
-      <unit id="org.bouncycastle.bcprov" version="1.70.0.v20220507-1208"/>
-      <unit id="org.bouncycastle.bcprov.source" version="1.70.0.v20220507-1208"/>
-      <unit id="org.bouncycastle.bcutil" version="1.70.0.v20220105-1522"/>
-      <unit id="org.bouncycastle.bcutil.source" version="1.70.0.v20220105-1522"/>
+      <unit id="org.bouncycastle.bcpg" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcpg.source" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcpkix" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcpkix.source" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcprov" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcprov.source" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcutil" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcutil.source" version="1.71.0.v20220723-1943"/>
       <unit id="org.hamcrest" version="2.2.0.v20210711-0821"/>
       <unit id="org.hamcrest.source" version="2.2.0.v20210711-0821"/>
       <unit id="org.hamcrest.core" version="1.3.0.v20180420-1519"/>
@@ -87,7 +87,7 @@
       <unit id="org.slf4j.binding.simple.source" version="1.7.30.v20200204-2150"/>
       <unit id="org.tukaani.xz" version="1.9.0.v20210624-1259"/>
       <unit id="org.tukaani.xz.source" version="1.9.0.v20210624-1259"/>
-      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20220531185310/repository"/>
+      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20220830213456/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.20.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.tpd
index a3ea583..ccca715 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.tpd
@@ -1,7 +1,7 @@
 target "jgit-4.20" with source configurePhase
 
 include "projects/jetty-10.0.x.tpd"
-include "orbit/R20220531185310-2022-06.tpd"
+include "orbit/R20220830213456-2022-09.tpd"
 
 location "https://download.eclipse.org/releases/2021-06/" {
 	org.eclipse.osgi lazy
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.target
index 85ed31f..b98b23b 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.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.21" sequenceNumber="1654550635">
+<target name="jgit-4.21" sequenceNumber="1662389260">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="jakarta.servlet-api" version="4.0.0"/>
@@ -59,14 +59,14 @@
       <unit id="org.apache.sshd.sftp.source" version="2.8.0.v20211227-1750"/>
       <unit id="org.assertj" version="3.20.2.v20210706-1104"/>
       <unit id="org.assertj.source" version="3.20.2.v20210706-1104"/>
-      <unit id="org.bouncycastle.bcpg" version="1.70.0.v20220507-1208"/>
-      <unit id="org.bouncycastle.bcpg.source" version="1.70.0.v20220507-1208"/>
-      <unit id="org.bouncycastle.bcpkix" version="1.70.0.v20220105-1522"/>
-      <unit id="org.bouncycastle.bcpkix.source" version="1.70.0.v20220105-1522"/>
-      <unit id="org.bouncycastle.bcprov" version="1.70.0.v20220507-1208"/>
-      <unit id="org.bouncycastle.bcprov.source" version="1.70.0.v20220507-1208"/>
-      <unit id="org.bouncycastle.bcutil" version="1.70.0.v20220105-1522"/>
-      <unit id="org.bouncycastle.bcutil.source" version="1.70.0.v20220105-1522"/>
+      <unit id="org.bouncycastle.bcpg" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcpg.source" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcpkix" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcpkix.source" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcprov" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcprov.source" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcutil" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcutil.source" version="1.71.0.v20220723-1943"/>
       <unit id="org.hamcrest" version="2.2.0.v20210711-0821"/>
       <unit id="org.hamcrest.source" version="2.2.0.v20210711-0821"/>
       <unit id="org.hamcrest.core" version="1.3.0.v20180420-1519"/>
@@ -87,7 +87,7 @@
       <unit id="org.slf4j.binding.simple.source" version="1.7.30.v20200204-2150"/>
       <unit id="org.tukaani.xz" version="1.9.0.v20210624-1259"/>
       <unit id="org.tukaani.xz.source" version="1.9.0.v20210624-1259"/>
-      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20220531185310/repository"/>
+      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20220830213456/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.21.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.tpd
index 0808601..2652598 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.tpd
@@ -1,7 +1,7 @@
 target "jgit-4.21" with source configurePhase
 
 include "projects/jetty-10.0.x.tpd"
-include "orbit/R20220531185310-2022-06.tpd"
+include "orbit/R20220830213456-2022-09.tpd"
 
 location "https://download.eclipse.org/releases/2021-09/" {
 	org.eclipse.osgi lazy
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.22.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.22.target
index e7b5a31..7186609 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.22.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.22.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.22" sequenceNumber="1654550634">
+<target name="jgit-4.22" sequenceNumber="1662389260">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="jakarta.servlet-api" version="4.0.0"/>
@@ -59,14 +59,14 @@
       <unit id="org.apache.sshd.sftp.source" version="2.8.0.v20211227-1750"/>
       <unit id="org.assertj" version="3.20.2.v20210706-1104"/>
       <unit id="org.assertj.source" version="3.20.2.v20210706-1104"/>
-      <unit id="org.bouncycastle.bcpg" version="1.70.0.v20220507-1208"/>
-      <unit id="org.bouncycastle.bcpg.source" version="1.70.0.v20220507-1208"/>
-      <unit id="org.bouncycastle.bcpkix" version="1.70.0.v20220105-1522"/>
-      <unit id="org.bouncycastle.bcpkix.source" version="1.70.0.v20220105-1522"/>
-      <unit id="org.bouncycastle.bcprov" version="1.70.0.v20220507-1208"/>
-      <unit id="org.bouncycastle.bcprov.source" version="1.70.0.v20220507-1208"/>
-      <unit id="org.bouncycastle.bcutil" version="1.70.0.v20220105-1522"/>
-      <unit id="org.bouncycastle.bcutil.source" version="1.70.0.v20220105-1522"/>
+      <unit id="org.bouncycastle.bcpg" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcpg.source" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcpkix" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcpkix.source" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcprov" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcprov.source" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcutil" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcutil.source" version="1.71.0.v20220723-1943"/>
       <unit id="org.hamcrest" version="2.2.0.v20210711-0821"/>
       <unit id="org.hamcrest.source" version="2.2.0.v20210711-0821"/>
       <unit id="org.hamcrest.core" version="1.3.0.v20180420-1519"/>
@@ -87,7 +87,7 @@
       <unit id="org.slf4j.binding.simple.source" version="1.7.30.v20200204-2150"/>
       <unit id="org.tukaani.xz" version="1.9.0.v20210624-1259"/>
       <unit id="org.tukaani.xz.source" version="1.9.0.v20210624-1259"/>
-      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20220531185310/repository"/>
+      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20220830213456/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.22.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.22.tpd
index 5697574..bc87048 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.22.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.22.tpd
@@ -1,7 +1,7 @@
 target "jgit-4.22" with source configurePhase
 
 include "projects/jetty-10.0.x.tpd"
-include "orbit/R20220531185310-2022-06.tpd"
+include "orbit/R20220830213456-2022-09.tpd"
 
 location "https://download.eclipse.org/releases/2021-12/" {
 	org.eclipse.osgi lazy
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.23.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.23.target
index 3c18e4b..670d61d 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.23.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.23.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.23" sequenceNumber="1654550634">
+<target name="jgit-4.23" sequenceNumber="1662389260">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="jakarta.servlet-api" version="4.0.0"/>
@@ -59,14 +59,14 @@
       <unit id="org.apache.sshd.sftp.source" version="2.8.0.v20211227-1750"/>
       <unit id="org.assertj" version="3.20.2.v20210706-1104"/>
       <unit id="org.assertj.source" version="3.20.2.v20210706-1104"/>
-      <unit id="org.bouncycastle.bcpg" version="1.70.0.v20220507-1208"/>
-      <unit id="org.bouncycastle.bcpg.source" version="1.70.0.v20220507-1208"/>
-      <unit id="org.bouncycastle.bcpkix" version="1.70.0.v20220105-1522"/>
-      <unit id="org.bouncycastle.bcpkix.source" version="1.70.0.v20220105-1522"/>
-      <unit id="org.bouncycastle.bcprov" version="1.70.0.v20220507-1208"/>
-      <unit id="org.bouncycastle.bcprov.source" version="1.70.0.v20220507-1208"/>
-      <unit id="org.bouncycastle.bcutil" version="1.70.0.v20220105-1522"/>
-      <unit id="org.bouncycastle.bcutil.source" version="1.70.0.v20220105-1522"/>
+      <unit id="org.bouncycastle.bcpg" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcpg.source" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcpkix" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcpkix.source" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcprov" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcprov.source" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcutil" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcutil.source" version="1.71.0.v20220723-1943"/>
       <unit id="org.hamcrest" version="2.2.0.v20210711-0821"/>
       <unit id="org.hamcrest.source" version="2.2.0.v20210711-0821"/>
       <unit id="org.hamcrest.core" version="1.3.0.v20180420-1519"/>
@@ -87,7 +87,7 @@
       <unit id="org.slf4j.binding.simple.source" version="1.7.30.v20200204-2150"/>
       <unit id="org.tukaani.xz" version="1.9.0.v20210624-1259"/>
       <unit id="org.tukaani.xz.source" version="1.9.0.v20210624-1259"/>
-      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20220531185310/repository"/>
+      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20220830213456/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.23.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.23.tpd
index 7fd421a..2d15b9f 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.23.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.23.tpd
@@ -1,7 +1,7 @@
 target "jgit-4.23" with source configurePhase
 
 include "projects/jetty-10.0.x.tpd"
-include "orbit/R20220531185310-2022-06.tpd"
+include "orbit/R20220830213456-2022-09.tpd"
 
 location "https://download.eclipse.org/releases/2022-03/" {
 	org.eclipse.osgi lazy
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.24.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.24.target
index b622b4e..4d78a59 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.24.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.24.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.24" sequenceNumber="1654550621">
+<target name="jgit-4.24" sequenceNumber="1662389260">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="jakarta.servlet-api" version="4.0.0"/>
@@ -59,14 +59,14 @@
       <unit id="org.apache.sshd.sftp.source" version="2.8.0.v20211227-1750"/>
       <unit id="org.assertj" version="3.20.2.v20210706-1104"/>
       <unit id="org.assertj.source" version="3.20.2.v20210706-1104"/>
-      <unit id="org.bouncycastle.bcpg" version="1.70.0.v20220507-1208"/>
-      <unit id="org.bouncycastle.bcpg.source" version="1.70.0.v20220507-1208"/>
-      <unit id="org.bouncycastle.bcpkix" version="1.70.0.v20220105-1522"/>
-      <unit id="org.bouncycastle.bcpkix.source" version="1.70.0.v20220105-1522"/>
-      <unit id="org.bouncycastle.bcprov" version="1.70.0.v20220507-1208"/>
-      <unit id="org.bouncycastle.bcprov.source" version="1.70.0.v20220507-1208"/>
-      <unit id="org.bouncycastle.bcutil" version="1.70.0.v20220105-1522"/>
-      <unit id="org.bouncycastle.bcutil.source" version="1.70.0.v20220105-1522"/>
+      <unit id="org.bouncycastle.bcpg" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcpg.source" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcpkix" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcpkix.source" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcprov" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcprov.source" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcutil" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcutil.source" version="1.71.0.v20220723-1943"/>
       <unit id="org.hamcrest" version="2.2.0.v20210711-0821"/>
       <unit id="org.hamcrest.source" version="2.2.0.v20210711-0821"/>
       <unit id="org.hamcrest.core" version="1.3.0.v20180420-1519"/>
@@ -87,11 +87,11 @@
       <unit id="org.slf4j.binding.simple.source" version="1.7.30.v20200204-2150"/>
       <unit id="org.tukaani.xz" version="1.9.0.v20210624-1259"/>
       <unit id="org.tukaani.xz.source" version="1.9.0.v20210624-1259"/>
-      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20220531185310/repository"/>
+      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20220830213456/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/2022-06/"/>
+      <repository location="https://download.eclipse.org/releases/2022-06/"/>
     </location>
   </locations>
 </target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.24.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.24.tpd
index e81eec9..0b7176a 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.24.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.24.tpd
@@ -1,8 +1,8 @@
 target "jgit-4.24" with source configurePhase
 
 include "projects/jetty-10.0.x.tpd"
-include "orbit/R20220531185310-2022-06.tpd"
+include "orbit/R20220830213456-2022-09.tpd"
 
-location "https://download.eclipse.org/staging/2022-06/" {
+location "https://download.eclipse.org/releases/2022-06/" {
 	org.eclipse.osgi lazy
 }
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.25.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.25.target
new file mode 100644
index 0000000..568da28
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.25.target
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<?pde?>
+<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
+<target name="jgit-4.25" sequenceNumber="1662389260">
+  <locations>
+    <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
+      <unit id="jakarta.servlet-api" version="4.0.0"/>
+      <unit id="jakarta.servlet-api.source" version="4.0.0"/>
+      <unit id="org.eclipse.jetty.http" version="10.0.6"/>
+      <unit id="org.eclipse.jetty.http.source" version="10.0.6"/>
+      <unit id="org.eclipse.jetty.io" version="10.0.6"/>
+      <unit id="org.eclipse.jetty.io.source" version="10.0.6"/>
+      <unit id="org.eclipse.jetty.security" version="10.0.6"/>
+      <unit id="org.eclipse.jetty.security.source" version="10.0.6"/>
+      <unit id="org.eclipse.jetty.server" version="10.0.6"/>
+      <unit id="org.eclipse.jetty.server.source" version="10.0.6"/>
+      <unit id="org.eclipse.jetty.servlet" version="10.0.6"/>
+      <unit id="org.eclipse.jetty.servlet.source" version="10.0.6"/>
+      <unit id="org.eclipse.jetty.util" version="10.0.6"/>
+      <unit id="org.eclipse.jetty.util.source" version="10.0.6"/>
+      <unit id="org.eclipse.jetty.util.ajax" version="10.0.6"/>
+      <unit id="org.eclipse.jetty.util.ajax.source" version="10.0.6"/>
+      <repository id="jetty-10.0.x" location="https://download.eclipse.org/eclipse/jetty/10.0.6/"/>
+    </location>
+    <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
+      <unit id="com.google.gson" version="2.8.9.v20220111-1409"/>
+      <unit id="com.google.gson.source" version="2.8.9.v20220111-1409"/>
+      <unit id="com.jcraft.jsch" version="0.1.55.v20190404-1902"/>
+      <unit id="com.jcraft.jsch.source" version="0.1.55.v20190404-1902"/>
+      <unit id="com.jcraft.jzlib" version="1.1.3.v20220502-1820"/>
+      <unit id="com.jcraft.jzlib.source" version="1.1.3.v20220502-1820"/>
+      <unit id="com.sun.jna" version="5.8.0.v20210503-0343"/>
+      <unit id="com.sun.jna.source" version="5.8.0.v20210503-0343"/>
+      <unit id="com.sun.jna.platform" version="5.8.0.v20210406-1004"/>
+      <unit id="com.sun.jna.platform.source" version="5.8.0.v20210406-1004"/>
+      <unit id="javaewah" version="1.1.13.v20211029-0839"/>
+      <unit id="javaewah.source" version="1.1.13.v20211029-0839"/>
+      <unit id="net.bytebuddy.byte-buddy" version="1.9.0.v20181107-1410"/>
+      <unit id="net.bytebuddy.byte-buddy-agent" version="1.9.0.v20181106-1534"/>
+      <unit id="net.bytebuddy.byte-buddy-agent.source" version="1.9.0.v20181106-1534"/>
+      <unit id="net.bytebuddy.byte-buddy.source" version="1.9.0.v20181107-1410"/>
+      <unit id="net.i2p.crypto.eddsa" version="0.3.0.v20220506-1020"/>
+      <unit id="net.i2p.crypto.eddsa.source" version="0.3.0.v20220506-1020"/>
+      <unit id="org.apache.ant" version="1.10.12.v20211102-1452"/>
+      <unit id="org.apache.ant.source" version="1.10.12.v20211102-1452"/>
+      <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.21.0.v20211103-2100"/>
+      <unit id="org.apache.commons.compress.source" version="1.21.0.v20211103-2100"/>
+      <unit id="org.apache.commons.logging" version="1.2.0.v20180409-1502"/>
+      <unit id="org.apache.commons.logging.source" version="1.2.0.v20180409-1502"/>
+      <unit id="org.apache.httpcomponents.httpclient" version="4.5.13.v20210128-2225"/>
+      <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.13.v20210128-2225"/>
+      <unit id="org.apache.httpcomponents.httpcore" version="4.4.15.v20220209-2345"/>
+      <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.15.v20220209-2345"/>
+      <unit id="org.apache.sshd.osgi" version="2.8.0.v20211227-1750"/>
+      <unit id="org.apache.sshd.osgi.source" version="2.8.0.v20211227-1750"/>
+      <unit id="org.apache.sshd.sftp" version="2.8.0.v20211227-1750"/>
+      <unit id="org.apache.sshd.sftp.source" version="2.8.0.v20211227-1750"/>
+      <unit id="org.assertj" version="3.20.2.v20210706-1104"/>
+      <unit id="org.assertj.source" version="3.20.2.v20210706-1104"/>
+      <unit id="org.bouncycastle.bcpg" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcpg.source" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcpkix" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcpkix.source" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcprov" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcprov.source" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcutil" version="1.71.0.v20220723-1943"/>
+      <unit id="org.bouncycastle.bcutil.source" version="1.71.0.v20220723-1943"/>
+      <unit id="org.hamcrest" version="2.2.0.v20210711-0821"/>
+      <unit id="org.hamcrest.source" version="2.2.0.v20210711-0821"/>
+      <unit id="org.hamcrest.core" version="1.3.0.v20180420-1519"/>
+      <unit id="org.hamcrest.core.source" version="1.3.0.v20180420-1519"/>
+      <unit id="org.hamcrest.library" version="1.3.0.v20180524-2246"/>
+      <unit id="org.hamcrest.library.source" version="1.3.0.v20180524-2246"/>
+      <unit id="org.junit" version="4.13.2.v20211018-1956"/>
+      <unit id="org.junit.source" version="4.13.2.v20211018-1956"/>
+      <unit id="org.kohsuke.args4j" version="2.33.0.v20160323-2218"/>
+      <unit id="org.kohsuke.args4j.source" version="2.33.0.v20160323-2218"/>
+      <unit id="org.mockito" version="2.23.0.v20200310-1642"/>
+      <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.30.v20200204-2150"/>
+      <unit id="org.slf4j.api.source" version="1.7.30.v20200204-2150"/>
+      <unit id="org.slf4j.binding.simple" version="1.7.30.v20200204-2150"/>
+      <unit id="org.slf4j.binding.simple.source" version="1.7.30.v20200204-2150"/>
+      <unit id="org.tukaani.xz" version="1.9.0.v20210624-1259"/>
+      <unit id="org.tukaani.xz.source" version="1.9.0.v20210624-1259"/>
+      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20220830213456/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/2022-09/"/>
+    </location>
+  </locations>
+</target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.25.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.25.tpd
new file mode 100644
index 0000000..0005893
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.25.tpd
@@ -0,0 +1,8 @@
+target "jgit-4.25" with source configurePhase
+
+include "projects/jetty-10.0.x.tpd"
+include "orbit/R20220830213456-2022-09.tpd"
+
+location "https://download.eclipse.org/staging/2022-09/" {
+	org.eclipse.osgi lazy
+}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20220830213456-2022-09.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20220830213456-2022-09.tpd
new file mode 100644
index 0000000..8db1018
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20220830213456-2022-09.tpd
@@ -0,0 +1,69 @@
+target "R20220830213456-2022-09" with source configurePhase
+// see https://download.eclipse.org/tools/orbit/downloads/
+
+location "https://download.eclipse.org/tools/orbit/downloads/drops/R20220830213456/repository" {
+	com.google.gson [2.8.9.v20220111-1409,2.8.9.v20220111-1409]
+	com.google.gson.source [2.8.9.v20220111-1409,2.8.9.v20220111-1409]
+	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.3.v20220502-1820,1.1.3.v20220502-1820]
+	com.jcraft.jzlib.source [1.1.3.v20220502-1820,1.1.3.v20220502-1820]
+	com.sun.jna [5.8.0.v20210503-0343,5.8.0.v20210503-0343]
+	com.sun.jna.source [5.8.0.v20210503-0343,5.8.0.v20210503-0343]
+	com.sun.jna.platform [5.8.0.v20210406-1004,5.8.0.v20210406-1004]
+	com.sun.jna.platform.source [5.8.0.v20210406-1004,5.8.0.v20210406-1004]
+	javaewah [1.1.13.v20211029-0839,1.1.13.v20211029-0839]
+	javaewah.source [1.1.13.v20211029-0839,1.1.13.v20211029-0839]
+	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.v20220506-1020,0.3.0.v20220506-1020]
+	net.i2p.crypto.eddsa.source [0.3.0.v20220506-1020,0.3.0.v20220506-1020]
+	org.apache.ant [1.10.12.v20211102-1452,1.10.12.v20211102-1452]
+	org.apache.ant.source [1.10.12.v20211102-1452,1.10.12.v20211102-1452]
+	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.21.0.v20211103-2100,1.21.0.v20211103-2100]
+	org.apache.commons.compress.source [1.21.0.v20211103-2100,1.21.0.v20211103-2100]
+	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.13.v20210128-2225,4.5.13.v20210128-2225]
+	org.apache.httpcomponents.httpclient.source [4.5.13.v20210128-2225,4.5.13.v20210128-2225]
+	org.apache.httpcomponents.httpcore [4.4.15.v20220209-2345,4.4.15.v20220209-2345]
+	org.apache.httpcomponents.httpcore.source [4.4.15.v20220209-2345,4.4.15.v20220209-2345]
+	org.apache.sshd.osgi [2.8.0.v20211227-1750,2.8.0.v20211227-1750]
+	org.apache.sshd.osgi.source [2.8.0.v20211227-1750,2.8.0.v20211227-1750]
+	org.apache.sshd.sftp [2.8.0.v20211227-1750,2.8.0.v20211227-1750]
+	org.apache.sshd.sftp.source [2.8.0.v20211227-1750,2.8.0.v20211227-1750]
+	org.assertj [3.20.2.v20210706-1104,3.20.2.v20210706-1104]
+	org.assertj.source [3.20.2.v20210706-1104,3.20.2.v20210706-1104]
+	org.bouncycastle.bcpg [1.71.0.v20220723-1943,1.71.0.v20220723-1943]
+	org.bouncycastle.bcpg.source [1.71.0.v20220723-1943,1.71.0.v20220723-1943]
+	org.bouncycastle.bcpkix [1.71.0.v20220723-1943,1.71.0.v20220723-1943]
+	org.bouncycastle.bcpkix.source [1.71.0.v20220723-1943,1.71.0.v20220723-1943]
+	org.bouncycastle.bcprov [1.71.0.v20220723-1943,1.71.0.v20220723-1943]
+	org.bouncycastle.bcprov.source [1.71.0.v20220723-1943,1.71.0.v20220723-1943]
+	org.bouncycastle.bcutil [1.71.0.v20220723-1943,1.71.0.v20220723-1943]
+	org.bouncycastle.bcutil.source [1.71.0.v20220723-1943,1.71.0.v20220723-1943]
+	org.hamcrest [2.2.0.v20210711-0821,2.2.0.v20210711-0821]
+	org.hamcrest.source [2.2.0.v20210711-0821,2.2.0.v20210711-0821]
+	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.2.v20211018-1956,4.13.2.v20211018-1956]
+	org.junit.source [4.13.2.v20211018-1956,4.13.2.v20211018-1956]
+	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.simple [1.7.30.v20200204-2150,1.7.30.v20200204-2150]
+	org.slf4j.binding.simple.source [1.7.30.v20200204-2150,1.7.30.v20200204-2150]
+	org.tukaani.xz [1.9.0.v20210624-1259,1.9.0.v20210624-1259]
+	org.tukaani.xz.source [1.9.0.v20210624-1259,1.9.0.v20210624-1259]
+}
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 8152b85..0baf5ac 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>6.2.1-SNAPSHOT</version>
+    <version>6.3.1-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 812916e..bebd2a4 100644
--- a/org.eclipse.jgit.packaging/pom.xml
+++ b/org.eclipse.jgit.packaging/pom.xml
@@ -16,14 +16,14 @@
 
   <groupId>org.eclipse.jgit</groupId>
   <artifactId>jgit.tycho.parent</artifactId>
-  <version>6.2.1-SNAPSHOT</version>
+  <version>6.3.1-SNAPSHOT</version>
   <packaging>pom</packaging>
 
   <name>JGit Tycho Parent</name>
 
   <properties>
     <java.version>11</java.version>
-    <tycho-version>2.6.0</tycho-version>
+    <tycho-version>2.7.5</tycho-version>
     <tycho-extras-version>${tycho-version}</tycho-extras-version>
     <target-platform>jgit-4.17</target-platform>
   </properties>
@@ -259,6 +259,11 @@
                 <ws>cocoa</ws>
                 <arch>x86_64</arch>
               </environment>
+              <environment>
+                <os>macosx</os>
+                <ws>cocoa</ws>
+                <arch>aarch64</arch>
+              </environment>
             </environments>
           </configuration>
         </plugin>
diff --git a/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF
index dade9a8..cd947f0 100644
--- a/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF
@@ -3,30 +3,30 @@
 Bundle-Name: %Bundle-Name
 Automatic-Module-Name: org.eclipse.jgit.pgm.test
 Bundle-SymbolicName: org.eclipse.jgit.pgm.test
-Bundle-Version: 6.2.1.qualifier
+Bundle-Version: 6.3.1.qualifier
 Bundle-Vendor: %Bundle-Vendor
 Bundle-Localization: plugin
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: JavaSE-11
-Import-Package: org.eclipse.jgit.api;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.api.errors;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.diff;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.dircache;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.internal.diffmergetool;version="6.2.1",
- org.eclipse.jgit.internal.storage.file;version="6.2.1",
- org.eclipse.jgit.junit;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.lib;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.lib.internal;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.merge;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.pgm;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.pgm.internal;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.pgm.opt;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.revwalk;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.storage.file;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.transport;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.treewalk;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.util;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.util.io;version="[6.2.1,6.3.0)",
+Import-Package: org.eclipse.jgit.api;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.api.errors;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.diff;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.dircache;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.internal.diffmergetool;version="6.3.1",
+ org.eclipse.jgit.internal.storage.file;version="6.3.1",
+ org.eclipse.jgit.junit;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.lib;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.lib.internal;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.merge;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.pgm;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.pgm.internal;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.pgm.opt;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.revwalk;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.storage.file;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.transport;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.treewalk;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.util;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.util.io;version="[6.3.1,6.4.0)",
  org.hamcrest.core;bundle-version="[1.1.0,3.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 6748220..fd87d92 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>6.2.1-SNAPSHOT</version>
+    <version>6.3.1-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.pgm.test</artifactId>
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/DiffToolTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/DiffToolTest.java
index 2b50d45..f0908ce 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/DiffToolTest.java
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/DiffToolTest.java
@@ -81,6 +81,8 @@ public void testUserToolWithCommandNotFoundError() throws Exception {
 
 	@Test(expected = Die.class)
 	public void testEmptyToolName() throws Exception {
+		assumeLinuxPlatform();
+
 		String emptyToolName = "";
 
 		StoredConfig config = db.getConfig();
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/MergeToolTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/MergeToolTest.java
index 1236dd3..54c4f26 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/MergeToolTest.java
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/MergeToolTest.java
@@ -79,6 +79,8 @@ public void testUserToolWithCommandNotFoundError() throws Exception {
 
 	@Test
 	public void testEmptyToolName() throws Exception {
+		assumeLinuxPlatform();
+
 		String emptyToolName = "";
 
 		StoredConfig config = db.getConfig();
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ToolTestCase.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ToolTestCase.java
index a3c41f0..55db1a1 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ToolTestCase.java
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ToolTestCase.java
@@ -9,6 +9,7 @@
  */
 package org.eclipse.jgit.pgm;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
@@ -32,6 +33,8 @@
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.treewalk.FileTreeIterator;
 import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.util.SystemReader;
+import org.junit.Assume;
 import org.junit.Before;
 import org.kohsuke.args4j.Argument;
 import org.kohsuke.args4j.CmdLineException;
@@ -211,7 +214,7 @@ protected static InputStream createInputStream(String[] inputLines) {
 
 	protected static InputStream createInputStream(List<String> inputLines) {
 		String input = String.join(System.lineSeparator(), inputLines);
-		InputStream inputStream = new ByteArrayInputStream(input.getBytes());
+		InputStream inputStream = new ByteArrayInputStream(input.getBytes(UTF_8));
 		return inputStream;
 	}
 
@@ -240,4 +243,9 @@ protected static void assertArrayOfMatchingLines(String failMessage,
 					matches);
 		}
 	}
+
+	protected static void assumeLinuxPlatform() {
+		Assume.assumeTrue("This test can run only in Linux tests",
+				SystemReader.getInstance().isLinux());
+	}
 }
diff --git a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
index 5c351e5..39ea131 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: 6.2.1.qualifier
+Bundle-Version: 6.3.1.qualifier
 Bundle-Vendor: %Bundle-Vendor
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-11
@@ -14,49 +14,49 @@
  org.eclipse.jetty.servlet;version="[10.0.0,11.0.0)",
  org.eclipse.jetty.util;version="[10.0.0,11.0.0)",
  org.eclipse.jetty.util.component;version="[10.0.0,11.0.0)",
- org.eclipse.jgit.api;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.api.errors;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.archive;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.awtui;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.blame;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.diff;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.dircache;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.errors;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.gitrepo;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.internal.storage.file;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.internal.diffmergetool;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.internal.storage.io;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.internal.storage.pack;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.internal.storage.reftable;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.lfs;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.lfs.server;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.lfs.server.fs;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.lfs.server.s3;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.lib;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.merge;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.lib.internal;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.nls;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.notes;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.revplot;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.revwalk;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.revwalk.filter;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.storage.file;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.storage.pack;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.transport;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.transport.http.apache;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.transport.resolver;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.transport.ssh.jsch;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.transport.sshd;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.treewalk;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.treewalk.filter;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.util;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.util.io;version="[6.2.1,6.3.0)",
+ org.eclipse.jgit.api;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.api.errors;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.archive;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.awtui;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.blame;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.diff;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.dircache;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.errors;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.gitrepo;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.internal.diffmergetool;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.internal.storage.io;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.internal.storage.reftable;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.lfs;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.lfs.server;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.lfs.server.fs;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.lfs.server.s3;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.lib;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.merge;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.lib.internal;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.nls;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.notes;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.revplot;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.revwalk;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.revwalk.filter;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.storage.file;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.storage.pack;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.transport;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.transport.http.apache;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.transport.resolver;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.transport.ssh.jsch;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.transport.sshd;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.treewalk;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.treewalk.filter;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.util;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.util.io;version="[6.3.1,6.4.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="6.2.1";
+Export-Package: org.eclipse.jgit.console;version="6.3.1";
  uses:="org.eclipse.jgit.transport,
   org.eclipse.jgit.util",
- org.eclipse.jgit.pgm;version="6.2.1";
+ org.eclipse.jgit.pgm;version="6.3.1";
   uses:="org.eclipse.jgit.transport,
    org.eclipse.jgit.util.io,
    org.eclipse.jgit.awtui,
@@ -68,14 +68,14 @@
    org.eclipse.jgit.treewalk,
    org.eclipse.jgit.api,
    javax.swing",
- org.eclipse.jgit.pgm.debug;version="6.2.1";
+ org.eclipse.jgit.pgm.debug;version="6.3.1";
   uses:="org.eclipse.jgit.util.io,
    org.eclipse.jgit.pgm,
    org.eclipse.jetty.servlet",
- org.eclipse.jgit.pgm.internal;version="6.2.1";
+ org.eclipse.jgit.pgm.internal;version="6.3.1";
   x-friends:="org.eclipse.jgit.pgm.test,
    org.eclipse.jgit.test",
- org.eclipse.jgit.pgm.opt;version="6.2.1";
+ org.eclipse.jgit.pgm.opt;version="6.3.1";
   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 616f01b..4850be9 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: 6.2.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="6.2.1.qualifier";roots="."
+Bundle-Version: 6.3.1.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="6.3.1.qualifier";roots="."
diff --git a/org.eclipse.jgit.pgm/pom.xml b/org.eclipse.jgit.pgm/pom.xml
index 0c0f336..2a9f5d7 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>6.2.1-SNAPSHOT</version>
+    <version>6.3.1-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.pgm</artifactId>
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/DiffTool.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/DiffTool.java
index 3e6042a..87c7179 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/DiffTool.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/DiffTool.java
@@ -19,7 +19,7 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStreamReader;
-import java.nio.charset.StandardCharsets;
+import java.nio.charset.Charset;
 import java.text.MessageFormat;
 import java.util.List;
 import java.util.Map;
@@ -63,6 +63,7 @@
 import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
 import org.eclipse.jgit.treewalk.filter.TreeFilter;
 import org.eclipse.jgit.util.FS.ExecutionResult;
+import org.eclipse.jgit.util.SystemReader;
 import org.kohsuke.args4j.Argument;
 import org.kohsuke.args4j.Option;
 
@@ -138,7 +139,8 @@ protected void init(Repository repository, String gitDir) {
 		super.init(repository, gitDir);
 		diffFmt = new DiffFormatter(new BufferedOutputStream(outs));
 		diffTools = new DiffTools(repository);
-		inputReader = new BufferedReader(new InputStreamReader(ins, StandardCharsets.UTF_8));
+		inputReader = new BufferedReader(new InputStreamReader(ins,
+				SystemReader.getInstance().getDefaultCharset()));
 	}
 
 	@Override
@@ -243,11 +245,15 @@ private void compare(List<DiffEntry> files) throws IOException {
 						// TODO: check how to return the exit-code of the tool
 						// to jgit / java runtime ?
 						// int rc =...
+						Charset defaultCharset = SystemReader.getInstance()
+								.getDefaultCharset();
 						outw.println(
-								new String(result.getStdout().toByteArray()));
+								new String(result.getStdout().toByteArray(),
+										defaultCharset));
 						outw.flush();
 						errw.println(
-								new String(result.getStderr().toByteArray()));
+								new String(result.getStderr().toByteArray(),
+										defaultCharset));
 						errw.flush();
 					}
 				} catch (ToolException e) {
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/MergeTool.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/MergeTool.java
index 2a411b8..a382fab 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/MergeTool.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/MergeTool.java
@@ -18,6 +18,7 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStreamReader;
+import java.nio.charset.Charset;
 import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -55,6 +56,7 @@
 import org.eclipse.jgit.treewalk.WorkingTreeOptions;
 import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
 import org.eclipse.jgit.util.FS.ExecutionResult;
+import org.eclipse.jgit.util.SystemReader;
 import org.kohsuke.args4j.Argument;
 import org.kohsuke.args4j.Option;
 import org.kohsuke.args4j.spi.RestOfArgumentsHandler;
@@ -108,7 +110,9 @@ void noGui(@SuppressWarnings("unused") boolean on) {
 	protected void init(Repository repository, String gitDir) {
 		super.init(repository, gitDir);
 		mergeTools = new MergeTools(repository);
-		inputReader = new BufferedReader(new InputStreamReader(ins));
+		inputReader = new BufferedReader(
+				new InputStreamReader(ins,
+						SystemReader.getInstance().getDefaultCharset()));
 	}
 
 	enum MergeResult {
@@ -285,9 +289,13 @@ private MergeResult mergeModified(String mergedFilePath, boolean showPrompt)
 						gui, this::promptForLaunch, this::informUserNoTool);
 				if (optionalResult.isPresent()) {
 					ExecutionResult result = optionalResult.get();
-					outw.println(new String(result.getStdout().toByteArray()));
+					Charset defaultCharset = SystemReader.getInstance()
+							.getDefaultCharset();
+					outw.println(new String(result.getStdout().toByteArray(),
+							defaultCharset));
 					outw.flush();
-					errw.println(new String(result.getStderr().toByteArray()));
+					errw.println(new String(result.getStderr().toByteArray(),
+							defaultCharset));
 					errw.flush();
 				} else {
 					return MergeResult.ABORTED;
diff --git a/org.eclipse.jgit.ssh.apache.agent/META-INF/MANIFEST.MF b/org.eclipse.jgit.ssh.apache.agent/META-INF/MANIFEST.MF
index 8c52337..ac14d95 100644
--- a/org.eclipse.jgit.ssh.apache.agent/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ssh.apache.agent/META-INF/MANIFEST.MF
@@ -2,16 +2,16 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %Bundle-Name
 Bundle-SymbolicName: org.eclipse.jgit.ssh.apache.agent;singleton:=true
-Bundle-Version: 6.2.1.qualifier
+Bundle-Version: 6.3.1.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %Bundle-Vendor
-Fragment-Host: org.eclipse.jgit.ssh.apache;bundle-version="[6.2.1,6.3.0)"
+Fragment-Host: org.eclipse.jgit.ssh.apache;bundle-version="[6.3.1,6.4.0)"
 Bundle-ActivationPolicy: lazy
 Automatic-Module-Name: org.eclipse.jgit.ssh.apache.agent
 Bundle-RequiredExecutionEnvironment: JavaSE-11
-Import-Package: org.eclipse.jgit.transport.sshd;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.nls;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.util;version="[6.2.1,6.3.0)"
+Import-Package: org.eclipse.jgit.transport.sshd;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.nls;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.util;version="[6.3.1,6.4.0)"
 Require-Bundle: com.sun.jna;bundle-version="[5.8.0,6.0.0)",
  com.sun.jna.platform;bundle-version="[5.8.0,6.0.0)"
-Export-Package: org.eclipse.jgit.internal.transport.sshd.agent.connector;version="6.2.1";x-internal:=true
+Export-Package: org.eclipse.jgit.internal.transport.sshd.agent.connector;version="6.3.1";x-internal:=true
diff --git a/org.eclipse.jgit.ssh.apache.agent/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.ssh.apache.agent/META-INF/SOURCE-MANIFEST.MF
index 3433630..89a56b7 100644
--- a/org.eclipse.jgit.ssh.apache.agent/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.ssh.apache.agent/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@
 Bundle-Name: org.eclipse.jgit.ssh.apache.agent - Sources
 Bundle-SymbolicName: org.eclipse.jgit.ssh.apache.agent.source
 Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 6.2.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.ssh.apache.agent;version="6.2.1.qualifier";roots="."
+Bundle-Version: 6.3.1.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.ssh.apache.agent;version="6.3.1.qualifier";roots="."
diff --git a/org.eclipse.jgit.ssh.apache.agent/pom.xml b/org.eclipse.jgit.ssh.apache.agent/pom.xml
index 5211055..e43e3fc 100644
--- a/org.eclipse.jgit.ssh.apache.agent/pom.xml
+++ b/org.eclipse.jgit.ssh.apache.agent/pom.xml
@@ -17,7 +17,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>6.2.1-SNAPSHOT</version>
+    <version>6.3.1-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.ssh.apache.agent</artifactId>
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 fe75eee..68b9ea0 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: 6.2.1.qualifier
+Bundle-Version: 6.3.1.qualifier
 Bundle-Vendor: %Bundle-Vendor
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-11
@@ -21,16 +21,16 @@
  org.apache.sshd.core;version="[2.8.0,2.9.0)",
  org.apache.sshd.server;version="[2.8.0,2.9.0)",
  org.apache.sshd.server.forward;version="[2.8.0,2.9.0)",
- org.eclipse.jgit.api;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.api.errors;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.internal.transport.sshd.proxy;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.junit;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.junit.ssh;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.lib;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.transport;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.transport.sshd;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.transport.sshd.agent;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.util;version="[6.2.1,6.3.0)",
+ org.eclipse.jgit.api;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.api.errors;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.internal.transport.sshd.proxy;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.junit;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.junit.ssh;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.lib;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.transport;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.transport.sshd;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.transport.sshd.agent;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.util;version="[6.3.1,6.4.0)",
  org.hamcrest;version="[1.1.0,3.0.0)",
  org.junit;version="[4.13,5.0.0)",
  org.junit.experimental.theories;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 6486999..fd882bd 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>6.2.1-SNAPSHOT</version>
+    <version>6.3.1-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.ssh.apache.test</artifactId>
diff --git a/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF b/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF
index 941632d..0ff3c8d 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: 6.2.1.qualifier
+Bundle-Version: 6.3.1.qualifier
 Bundle-RequiredExecutionEnvironment: JavaSE-11
-Export-Package: org.eclipse.jgit.internal.transport.sshd;version="6.2.1";x-internal:=true;
+Export-Package: org.eclipse.jgit.internal.transport.sshd;version="6.3.1";x-internal:=true;
   uses:="org.apache.sshd.client,
    org.apache.sshd.client.auth,
    org.apache.sshd.client.auth.keyboard,
@@ -23,17 +23,17 @@
    org.apache.sshd.common.signature,
    org.apache.sshd.common.util.buffer,
    org.eclipse.jgit.transport",
- org.eclipse.jgit.internal.transport.sshd.agent;version="6.2.1";x-internal:=true,
- org.eclipse.jgit.internal.transport.sshd.auth;version="6.2.1";x-internal:=true,
- org.eclipse.jgit.internal.transport.sshd.proxy;version="6.2.1";x-friends:="org.eclipse.jgit.ssh.apache.test",
- org.eclipse.jgit.transport.sshd;version="6.2.1";
+ org.eclipse.jgit.internal.transport.sshd.agent;version="6.3.1";x-internal:=true,
+ org.eclipse.jgit.internal.transport.sshd.auth;version="6.3.1";x-internal:=true,
+ org.eclipse.jgit.internal.transport.sshd.proxy;version="6.3.1";x-friends:="org.eclipse.jgit.ssh.apache.test",
+ org.eclipse.jgit.transport.sshd;version="6.3.1";
   uses:="org.eclipse.jgit.transport,
    org.apache.sshd.client.config.hosts,
    org.apache.sshd.common.keyprovider,
    org.eclipse.jgit.util,
    org.apache.sshd.client.session,
    org.apache.sshd.client.keyverifier",
- org.eclipse.jgit.transport.sshd.agent;version="6.2.1"
+ org.eclipse.jgit.transport.sshd.agent;version="6.3.1"
 Import-Package: net.i2p.crypto.eddsa;version="[0.3.0,0.4.0)",
  org.apache.sshd.agent;version="[2.8.0,2.9.0)",
  org.apache.sshd.client;version="[2.8.0,2.9.0)",
@@ -86,12 +86,12 @@
  org.apache.sshd.sftp;version="[2.8.0,2.9.0)",
  org.apache.sshd.sftp.client;version="[2.8.0,2.9.0)",
  org.apache.sshd.sftp.common;version="[2.8.0,2.9.0)",
- org.eclipse.jgit.annotations;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.errors;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.fnmatch;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.internal.storage.file;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.internal.transport.ssh;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.nls;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.transport;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.util;version="[6.2.1,6.3.0)",
+ org.eclipse.jgit.annotations;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.errors;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.fnmatch;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.internal.transport.ssh;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.nls;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.transport;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.util;version="[6.3.1,6.4.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 2c3ac20..406a56d 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: 6.2.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.ssh.apache;version="6.2.1.qualifier";roots="."
+Bundle-Version: 6.3.1.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.ssh.apache;version="6.3.1.qualifier";roots="."
diff --git a/org.eclipse.jgit.ssh.apache/pom.xml b/org.eclipse.jgit.ssh.apache/pom.xml
index 1758431..3a97b3a 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>6.2.1-SNAPSHOT</version>
+    <version>6.3.1-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.ssh.apache</artifactId>
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthentication.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthentication.java
index 96da0cc..e1036c6 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthentication.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthentication.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018, 2021 Thomas Wolf <thomas.wolf@paranor.ch> and others
+ * Copyright (C) 2018, 2022 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
@@ -99,13 +99,18 @@ public void init(ClientSession rawSession, String service)
 					log.debug(PUBKEY_ACCEPTED_ALGORITHMS + ' ' + signatures);
 				}
 				setSignatureFactoriesNames(signatures);
-			} else {
-				log.warn(format(SshdText.get().configNoKnownAlgorithms,
-						PUBKEY_ACCEPTED_ALGORITHMS, pubkeyAlgos));
+				super.init(session, service);
+				return;
 			}
+			log.warn(format(SshdText.get().configNoKnownAlgorithms,
+					PUBKEY_ACCEPTED_ALGORITHMS, pubkeyAlgos));
 		}
-		// If we don't set signature factories here, the default ones from the
-		// session will be used.
+		// TODO: remove this once we're on an sshd version that has SSHD-1272
+		// fixed
+		List<NamedFactory<Signature>> localFactories = getSignatureFactories();
+		if (localFactories == null || localFactories.isEmpty()) {
+			setSignatureFactoriesNames(session.getSignatureFactoriesNames());
+		}
 		super.init(session, service);
 	}
 
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 9007e75..5a47db3 100644
--- a/org.eclipse.jgit.ssh.jsch.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ssh.jsch.test/META-INF/MANIFEST.MF
@@ -3,18 +3,18 @@
 Bundle-Name: %Bundle-Name
 Automatic-Module-Name: org.eclipse.jgit.ssh.jsch.test
 Bundle-SymbolicName: org.eclipse.jgit.ssh.jsch.test
-Bundle-Version: 6.2.1.qualifier
+Bundle-Version: 6.3.1.qualifier
 Bundle-Vendor: %Bundle-Vendor
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-11
 Import-Package: com.jcraft.jsch;version="[0.1.54,0.2.0)",
- org.eclipse.jgit.errors;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.junit;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.junit.ssh;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.lib;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.transport;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.transport.ssh.jsch;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.util;version="[6.2.1,6.3.0)",
+ org.eclipse.jgit.errors;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.junit;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.junit.ssh;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.lib;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.transport;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.transport.ssh.jsch;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.util;version="[6.3.1,6.4.0)",
  org.hamcrest;version="[1.1.0,3.0.0)",
  org.junit;version="[4.13,5.0.0)",
  org.junit.experimental.theories;version="[4.13,5.0.0)",
diff --git a/org.eclipse.jgit.ssh.jsch.test/pom.xml b/org.eclipse.jgit.ssh.jsch.test/pom.xml
index 59db255..2d5b023 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>6.2.1-SNAPSHOT</version>
+    <version>6.3.1-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 df1ba3b..18cb794 100644
--- a/org.eclipse.jgit.ssh.jsch/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ssh.jsch/META-INF/MANIFEST.MF
@@ -3,19 +3,19 @@
 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="[6.2.1,6.3.0)"
+Fragment-Host: org.eclipse.jgit;bundle-version="[6.3.1,6.4.0)"
 Bundle-Vendor: %Bundle-Vendor
 Bundle-Localization: plugin
 Bundle-ActivationPolicy: lazy
-Bundle-Version: 6.2.1.qualifier
+Bundle-Version: 6.3.1.qualifier
 Bundle-RequiredExecutionEnvironment: JavaSE-11
-Export-Package: org.eclipse.jgit.transport.ssh.jsch;version="6.2.1"
+Export-Package: org.eclipse.jgit.transport.ssh.jsch;version="6.3.1"
 Import-Package: com.jcraft.jsch;version="[0.1.37,0.2.0)",
- org.eclipse.jgit.errors;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.internal;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.internal.transport.ssh;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.nls;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.transport;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.util;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.util.io;version="[6.2.1,6.3.0)",
+ org.eclipse.jgit.errors;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.internal;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.internal.transport.ssh;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.nls;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.transport;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.util;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.util.io;version="[6.3.1,6.4.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 c3eb6c8..4ecfbe7 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: 6.2.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.ssh.jsch;version="6.2.1.qualifier";roots="."
+Bundle-Version: 6.3.1.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.ssh.jsch;version="6.3.1.qualifier";roots="."
diff --git a/org.eclipse.jgit.ssh.jsch/pom.xml b/org.eclipse.jgit.ssh.jsch/pom.xml
index f54cc2f..d1d2391 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>6.2.1-SNAPSHOT</version>
+    <version>6.3.1-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.ssh.jsch</artifactId>
diff --git a/org.eclipse.jgit.test/BUILD b/org.eclipse.jgit.test/BUILD
index 6f6c88c..258e845 100644
--- a/org.eclipse.jgit.test/BUILD
+++ b/org.eclipse.jgit.test/BUILD
@@ -13,6 +13,7 @@
 ) + [PKG + c for c in [
     "api/AbstractRemoteCommandTest.java",
     "diff/AbstractDiffTestCase.java",
+    "diff/AbstractRenameDetectionTestCase.java",
     "internal/diffmergetool/ExternalToolTestCase.java",
     "internal/revwalk/ObjectReachabilityTestCase.java",
     "internal/revwalk/ReachabilityCheckerTestCase.java",
diff --git a/org.eclipse.jgit.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.test/META-INF/MANIFEST.MF
index 381a719..97fe1be 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: 6.2.1.qualifier
+Bundle-Version: 6.3.1.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %Bundle-Vendor
 Bundle-RequiredExecutionEnvironment: JavaSE-11
@@ -16,61 +16,62 @@
  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="[6.2.1,6.3.0)",
- org.eclipse.jgit.api;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.api.errors;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.archive;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.attributes;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.awtui;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.blame;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.diff;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.dircache;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.errors;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.events;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.fnmatch;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.gitrepo;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.hooks;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.ignore;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.ignore.internal;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.internal;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.internal.diffmergetool;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.internal.fsck;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.internal.revwalk;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.internal.storage.dfs;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.internal.storage.file;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.internal.storage.io;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.internal.storage.pack;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.internal.storage.reftable;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.internal.transport.connectivity;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.internal.transport.http;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.internal.transport.parser;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.internal.transport.ssh;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.junit;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.junit.time;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.lfs;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.lib;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.lib.internal;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.logging;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.merge;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.nls;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.notes;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.patch;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.pgm;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.pgm.internal;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.revplot;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.revwalk;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.revwalk.filter;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.storage.file;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.storage.pack;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.submodule;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.transport;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.transport.http;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.transport.resolver;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.treewalk;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.treewalk.filter;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.util;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.util.io;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.util.sha1;version="[6.2.1,6.3.0)",
+ org.eclipse.jgit.annotations;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.api;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.api.errors;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.archive;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.attributes;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.awtui;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.blame;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.diff;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.dircache;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.errors;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.events;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.fnmatch;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.gitrepo;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.hooks;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.ignore;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.ignore.internal;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.internal;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.internal.diff;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.internal.diffmergetool;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.internal.fsck;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.internal.revwalk;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.internal.storage.io;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.internal.storage.reftable;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.internal.transport.connectivity;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.internal.transport.http;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.internal.transport.parser;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.internal.transport.ssh;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.junit;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.junit.time;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.lfs;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.lib;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.lib.internal;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.logging;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.merge;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.nls;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.notes;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.patch;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.pgm;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.pgm.internal;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.revplot;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.revwalk;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.revwalk.filter;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.storage.file;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.storage.pack;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.submodule;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.transport;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.transport.http;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.transport.resolver;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.treewalk;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.treewalk.filter;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.util;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.util.io;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.util.sha1;version="[6.3.1,6.4.0)",
  org.hamcrest;version="[1.1.0,3.0.0)",
  org.hamcrest.collection;version="[1.1.0,3.0.0)",
  org.junit;version="[4.13,5.0.0)",
@@ -85,4 +86,4 @@
  org.mockito.stubbing;version="[2.23.0,3.0.0)",
  org.objenesis;version="[2.6.0,3.0.0)",
  org.slf4j;version="[1.7.0,2.0.0)",
- org.tukaani.xz;version="[1.6.0,2.0)"
+ org.tukaani.xz
diff --git a/org.eclipse.jgit.test/pom.xml b/org.eclipse.jgit.test/pom.xml
index efba3e0..3eb762f 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>6.2.1-SNAPSHOT</version>
+    <version>6.3.1-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.test</artifactId>
@@ -42,19 +42,19 @@
     <!-- Optional security provider for encryption tests. -->
     <dependency>
       <groupId>org.bouncycastle</groupId>
-      <artifactId>bcprov-jdk15on</artifactId>
+      <artifactId>bcprov-jdk18on</artifactId>
       <scope>test</scope>
      </dependency>
 
     <dependency>
       <groupId>org.bouncycastle</groupId>
-      <artifactId>bcpg-jdk15on</artifactId>
+      <artifactId>bcpg-jdk18on</artifactId>
       <scope>test</scope>
     </dependency>
 
     <dependency>
       <groupId>org.bouncycastle</groupId>
-      <artifactId>bcpkix-jdk15on</artifactId>
+      <artifactId>bcpkix-jdk18on</artifactId>
       <scope>test</scope>
     </dependency>
 
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java
index 5dfdfcf..57661a7 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java
@@ -15,13 +15,16 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
 
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.nio.file.Files;
 import java.util.Set;
 
+import org.eclipse.jgit.api.ResetCommand.ResetType;
 import org.eclipse.jgit.api.errors.FilterFailedException;
 import org.eclipse.jgit.api.errors.GitAPIException;
 import org.eclipse.jgit.api.errors.NoFilepatternException;
@@ -34,6 +37,7 @@
 import org.eclipse.jgit.lfs.BuiltinLFS;
 import org.eclipse.jgit.lib.ConfigConstants;
 import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.CoreConfig.SymLinks;
 import org.eclipse.jgit.lib.FileMode;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectInserter;
@@ -100,6 +104,43 @@ public void testAddExistingSingleFile() throws IOException, GitAPIException {
 	}
 
 	@Test
+	public void testAddLink() throws IOException, GitAPIException {
+		assumeTrue(db.getFS().supportsSymlinks());
+		try (Git git = new Git(db)) {
+			writeTrashFile("a.txt", "a");
+			File link = new File(db.getWorkTree(), "link");
+			db.getFS().createSymLink(link, "a.txt");
+			git.add().addFilepattern(".").call();
+			assertEquals(
+					"[a.txt, mode:100644, content:a][link, mode:120000, content:a.txt]",
+					indexState(CONTENT));
+			git.commit().setMessage("link").call();
+			StoredConfig config = db.getConfig();
+			config.setEnum(ConfigConstants.CONFIG_CORE_SECTION, null,
+					ConfigConstants.CONFIG_KEY_SYMLINKS, SymLinks.FALSE);
+			config.save();
+			Files.delete(link.toPath());
+			git.reset().setMode(ResetType.HARD).call();
+			assertTrue(Files.isRegularFile(link.toPath()));
+			assertEquals(
+					"[a.txt, mode:100644, content:a][link, mode:120000, content:a.txt]",
+					indexState(CONTENT));
+			writeTrashFile("link", "b.txt");
+			git.add().addFilepattern("link").call();
+			assertEquals(
+					"[a.txt, mode:100644, content:a][link, mode:120000, content:b.txt]",
+					indexState(CONTENT));
+			config.setEnum(ConfigConstants.CONFIG_CORE_SECTION, null,
+					ConfigConstants.CONFIG_KEY_SYMLINKS, SymLinks.TRUE);
+			config.save();
+			git.add().addFilepattern("link").call();
+			assertEquals(
+					"[a.txt, mode:100644, content:a][link, mode:100644, content:b.txt]",
+					indexState(CONTENT));
+		}
+	}
+
+	@Test
 	public void testCleanFilter() throws IOException, GitAPIException {
 		writeTrashFile(".gitattributes", "*.txt filter=tstFilter");
 		writeTrashFile("src/a.tmp", "foo");
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ApplyCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ApplyCommandTest.java
index 584d149..40764b7 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ApplyCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ApplyCommandTest.java
@@ -525,9 +525,9 @@ public void testNonASCIIDel() throws Exception {
 	@Test
 	public void testRenameNoHunks() throws Exception {
 		ApplyResult result = init("RenameNoHunks", true, true);
-		assertEquals(1, result.getUpdatedFiles().size());
-		assertEquals(new File(db.getWorkTree(), "RenameNoHunks"), result.getUpdatedFiles()
-				.get(0));
+		assertEquals(2, result.getUpdatedFiles().size());
+		assertTrue(result.getUpdatedFiles().contains(new File(db.getWorkTree(), "RenameNoHunks")));
+		assertTrue(result.getUpdatedFiles().contains(new File(db.getWorkTree(), "nested/subdir/Renamed")));
 		checkFile(new File(db.getWorkTree(), "nested/subdir/Renamed"),
 				b.getString(0, b.size(), false));
 	}
@@ -535,9 +535,9 @@ public void testRenameNoHunks() throws Exception {
 	@Test
 	public void testRenameWithHunks() throws Exception {
 		ApplyResult result = init("RenameWithHunks", true, true);
-		assertEquals(1, result.getUpdatedFiles().size());
-		assertEquals(new File(db.getWorkTree(), "RenameWithHunks"), result.getUpdatedFiles()
-				.get(0));
+		assertEquals(2, result.getUpdatedFiles().size());
+		assertTrue(result.getUpdatedFiles().contains(new File(db.getWorkTree(), "RenameWithHunks")));
+		assertTrue(result.getUpdatedFiles().contains(new File(db.getWorkTree(), "nested/subdir/Renamed")));
 		checkFile(new File(db.getWorkTree(), "nested/subdir/Renamed"),
 				b.getString(0, b.size(), false));
 	}
@@ -546,7 +546,7 @@ public void testRenameWithHunks() throws Exception {
 	public void testCopyWithHunks() throws Exception {
 		ApplyResult result = init("CopyWithHunks", true, true);
 		assertEquals(1, result.getUpdatedFiles().size());
-		assertEquals(new File(db.getWorkTree(), "CopyWithHunks"), result.getUpdatedFiles()
+		assertEquals(new File(db.getWorkTree(), "CopyResult"), result.getUpdatedFiles()
 				.get(0));
 		checkFile(new File(db.getWorkTree(), "CopyResult"),
 				b.getString(0, b.size(), false));
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CleanCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CleanCommandTest.java
index f807889..204c89d 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CleanCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CleanCommandTest.java
@@ -301,4 +301,25 @@ public void testFilesShouldBeCleanedInSubSubFolders()
 		writeTrashFile("this_is/not_ok/more/subdirs/file.txt", "2");
 		git.clean().setCleanDirectories(true).setIgnore(false).call();
 	}
+
+	@Test
+	public void testPrefix() throws Exception {
+		File a = writeTrashFile("a.txt", "a");
+		File b = writeTrashFile("a/a.txt", "sub a");
+		File dir = b.getParentFile();
+		git.clean().call();
+		assertFalse(a.exists());
+		assertTrue(dir.exists());
+		assertTrue(b.exists());
+	}
+
+	@Test
+	public void testPrefixWithDir() throws Exception {
+		File a = writeTrashFile("a.txt", "a");
+		File b = writeTrashFile("a/a.txt", "sub a");
+		File dir = b.getParentFile();
+		git.clean().setCleanDirectories(true).call();
+		assertFalse(a.exists());
+		assertFalse(dir.exists());
+	}
 }
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 c928d2a..6053c8c 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
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011, 2013 Chris Aniszczyk <caniszczyk@gmail.com> and others
+ * Copyright (C) 2011, 2022 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
@@ -19,10 +19,14 @@
 import java.io.File;
 import java.io.IOException;
 import java.net.URISyntaxException;
+import java.time.Instant;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
 import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
 
 import org.eclipse.jgit.api.ListBranchCommand.ListMode;
 import org.eclipse.jgit.api.errors.GitAPIException;
@@ -40,6 +44,7 @@
 import org.eclipse.jgit.lib.StoredConfig;
 import org.eclipse.jgit.revwalk.RevBlob;
 import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevObject;
 import org.eclipse.jgit.submodule.SubmoduleStatus;
 import org.eclipse.jgit.submodule.SubmoduleStatusType;
 import org.eclipse.jgit.submodule.SubmoduleWalk;
@@ -895,6 +900,234 @@ public void testCloneWithHeadSymRefIsNonMasterCopy() throws IOException, GitAPIE
 		assertEquals("refs/heads/test-copy", git2.getRepository().getFullBranch());
 	}
 
+    @Test
+    public void testCloneRepositoryWithDepth() throws IOException, JGitInternalException, GitAPIException {
+		File directory = createTempDirectory("testCloneRepositoryWithDepth");
+		CloneCommand command = Git.cloneRepository();
+		command.setDirectory(directory);
+		command.setURI(fileUri());
+        command.setDepth(1);
+		command.setBranchesToClone(Set.of("refs/heads/test"));
+		Git git2 = command.call();
+		addRepoToClose(git2.getRepository());
+
+		List<RevCommit> log = StreamSupport.stream(git2.log().all().call().spliterator(), false)
+				.collect(Collectors.toList());
+		assertEquals(1, log.size());
+		RevCommit commit = log.get(0);
+		assertEquals(Set.of(commit.getId()),
+				git2.getRepository().getObjectDatabase().getShallowCommits());
+		assertEquals("Second commit", commit.getFullMessage());
+		assertEquals(0, commit.getParentCount());
+	}
+
+	@Test
+	public void testCloneRepositoryWithDepthAndAllBranches() throws IOException, JGitInternalException, GitAPIException {
+		File directory = createTempDirectory("testCloneRepositoryWithDepthAndAllBranches");
+		CloneCommand command = Git.cloneRepository();
+		command.setDirectory(directory);
+		command.setURI(fileUri());
+		command.setDepth(1);
+		command.setCloneAllBranches(true);
+		Git git2 = command.call();
+		addRepoToClose(git2.getRepository());
+
+		List<RevCommit> log = StreamSupport.stream(git2.log().all().call().spliterator(), false)
+				.collect(Collectors.toList());
+		assertEquals(2, log.size());
+		assertEquals(log.stream().map(RevCommit::getId).collect(Collectors.toSet()),
+				git2.getRepository().getObjectDatabase().getShallowCommits());
+		assertEquals(List.of("Second commit", "Initial commit"),
+				log.stream().map(RevCommit::getFullMessage).collect(Collectors.toList()));
+		for (RevCommit commit : log) {
+			assertEquals(0, commit.getParentCount());
+		}
+	}
+
+	@Test
+	public void testCloneRepositoryWithDepth2() throws Exception {
+		RevCommit parent = tr.git().log().call().iterator().next();
+		RevCommit commit = tr.commit()
+				.parent(parent)
+				.message("Third commit")
+				.add("test.txt", "Hello world")
+				.create();
+		tr.update("refs/heads/test", commit);
+
+		File directory = createTempDirectory("testCloneRepositoryWithDepth2");
+		CloneCommand command = Git.cloneRepository();
+		command.setDirectory(directory);
+		command.setURI(fileUri());
+		command.setDepth(2);
+		command.setBranchesToClone(Set.of("refs/heads/test"));
+		Git git2 = command.call();
+		addRepoToClose(git2.getRepository());
+
+		List<RevCommit> log = StreamSupport
+				.stream(git2.log().all().call().spliterator(), false)
+				.collect(Collectors.toList());
+		assertEquals(2, log.size());
+		assertEquals(Set.of(parent.getId()),
+				git2.getRepository().getObjectDatabase().getShallowCommits());
+		assertEquals(List.of("Third commit", "Second commit"), log.stream()
+				.map(RevCommit::getFullMessage).collect(Collectors.toList()));
+		assertEquals(List.of(Integer.valueOf(1), Integer.valueOf(0)),
+				log.stream().map(RevCommit::getParentCount)
+						.collect(Collectors.toList()));
+	}
+
+	@Test
+	public void testCloneRepositoryWithDepthAndFetch() throws Exception {
+		File directory = createTempDirectory("testCloneRepositoryWithDepthAndFetch");
+		CloneCommand command = Git.cloneRepository();
+		command.setDirectory(directory);
+		command.setURI(fileUri());
+		command.setDepth(1);
+		command.setBranchesToClone(Set.of("refs/heads/test"));
+		Git git2 = command.call();
+		addRepoToClose(git2.getRepository());
+
+		RevCommit parent = tr.git().log().call().iterator().next();
+		RevCommit commit = tr.commit()
+				.parent(parent)
+				.message("Third commit")
+				.add("test.txt", "Hello world")
+				.create();
+		tr.update("refs/heads/test", commit);
+
+		git2.fetch().call();
+
+		List<RevCommit> log = StreamSupport
+				.stream(git2.log().all().call().spliterator(), false)
+				.collect(Collectors.toList());
+		assertEquals(2, log.size());
+		assertEquals(Set.of(parent.getId()),
+				git2.getRepository().getObjectDatabase().getShallowCommits());
+		assertEquals(List.of("Third commit", "Second commit"), log.stream()
+				.map(RevCommit::getFullMessage).collect(Collectors.toList()));
+		assertEquals(List.of(Integer.valueOf(1), Integer.valueOf(0)),
+				log.stream().map(RevCommit::getParentCount)
+						.collect(Collectors.toList()));
+	}
+
+	@Test
+	public void testCloneRepositoryWithDepthAndFetchWithDepth() throws Exception {
+		File directory = createTempDirectory("testCloneRepositoryWithDepthAndFetchWithDepth");
+		CloneCommand command = Git.cloneRepository();
+		command.setDirectory(directory);
+		command.setURI(fileUri());
+		command.setDepth(1);
+		command.setBranchesToClone(Set.of("refs/heads/test"));
+		Git git2 = command.call();
+		addRepoToClose(git2.getRepository());
+
+		RevCommit parent = tr.git().log().call().iterator().next();
+		RevCommit commit = tr.commit()
+				.parent(parent)
+				.message("Third commit")
+				.add("test.txt", "Hello world")
+				.create();
+		tr.update("refs/heads/test", commit);
+
+		git2.fetch().setDepth(1).call();
+
+		List<RevCommit> log = StreamSupport
+				.stream(git2.log().all().call().spliterator(), false)
+				.collect(Collectors.toList());
+		assertEquals(2, log.size());
+		assertEquals(
+				log.stream().map(RevObject::getId).collect(Collectors.toSet()),
+				git2.getRepository().getObjectDatabase().getShallowCommits());
+		assertEquals(List.of("Third commit", "Second commit"), log.stream()
+				.map(RevCommit::getFullMessage).collect(Collectors.toList()));
+		assertEquals(List.of(Integer.valueOf(0), Integer.valueOf(0)),
+				log.stream().map(RevCommit::getParentCount)
+						.collect(Collectors.toList()));
+	}
+
+	@Test
+	public void testCloneRepositoryWithDepthAndFetchUnshallow() throws Exception {
+		File directory = createTempDirectory("testCloneRepositoryWithDepthAndFetchUnshallow");
+		CloneCommand command = Git.cloneRepository();
+		command.setDirectory(directory);
+		command.setURI(fileUri());
+		command.setDepth(1);
+		command.setBranchesToClone(Set.of("refs/heads/test"));
+		Git git2 = command.call();
+		addRepoToClose(git2.getRepository());
+
+		git2.fetch().setUnshallow(true).call();
+
+		List<RevCommit> log = StreamSupport
+				.stream(git2.log().all().call().spliterator(), false)
+				.collect(Collectors.toList());
+		assertEquals(2, log.size());
+		assertEquals(Set.of(),
+				git2.getRepository().getObjectDatabase().getShallowCommits());
+		assertEquals(List.of("Second commit", "Initial commit"), log.stream()
+				.map(RevCommit::getFullMessage).collect(Collectors.toList()));
+		assertEquals(List.of(Integer.valueOf(1), Integer.valueOf(0)),
+				log.stream().map(RevCommit::getParentCount)
+						.collect(Collectors.toList()));
+	}
+
+    @Test
+	public void testCloneRepositoryWithShallowSince() throws Exception {
+		RevCommit commit = tr.commit()
+				.parent(tr.git().log().call().iterator().next())
+				.message("Third commit").add("test.txt", "Hello world")
+				.create();
+		tr.update("refs/heads/test", commit);
+
+        File directory = createTempDirectory("testCloneRepositoryWithShallowSince");
+        CloneCommand command = Git.cloneRepository();
+        command.setDirectory(directory);
+        command.setURI(fileUri());
+        command.setShallowSince(Instant.ofEpochSecond(commit.getCommitTime()));
+        command.setBranchesToClone(Set.of("refs/heads/test"));
+        Git git2 = command.call();
+        addRepoToClose(git2.getRepository());
+
+		List<RevCommit> log = StreamSupport
+				.stream(git2.log().all().call().spliterator(), false)
+				.collect(Collectors.toList());
+		assertEquals(1, log.size());
+		assertEquals(Set.of(commit.getId()),
+				git2.getRepository().getObjectDatabase().getShallowCommits());
+		assertEquals("Third commit", log.get(0).getFullMessage());
+		assertEquals(0, log.get(0).getParentCount());
+    }
+
+	@Test
+	public void testCloneRepositoryWithShallowExclude() throws Exception {
+		RevCommit parent = tr.git().log().call().iterator().next();
+		tr.update("refs/heads/test",
+				tr.commit()
+					.parent(parent)
+					.message("Third commit")
+					.add("test.txt", "Hello world")
+					.create());
+
+		File directory = createTempDirectory("testCloneRepositoryWithShallowExclude");
+		CloneCommand command = Git.cloneRepository();
+		command.setDirectory(directory);
+		command.setURI(fileUri());
+		command.addShallowExclude(parent.getId());
+		command.setBranchesToClone(Set.of("refs/heads/test"));
+		Git git2 = command.call();
+		addRepoToClose(git2.getRepository());
+
+		List<RevCommit> log = StreamSupport
+				.stream(git2.log().all().call().spliterator(), false)
+				.collect(Collectors.toList());
+		assertEquals(1, log.size());
+		RevCommit commit = log.get(0);
+		assertEquals(Set.of(commit.getId()),
+				git2.getRepository().getObjectDatabase().getShallowCommits());
+		assertEquals("Third commit", commit.getFullMessage());
+		assertEquals(0, commit.getParentCount());
+	}
+
 	private void assertTagOption(Repository repo, TagOpt expectedTagOption)
 			throws URISyntaxException {
 		RemoteConfig remoteConfig = new RemoteConfig(
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/AbstractRenameDetectionTestCase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/AbstractRenameDetectionTestCase.java
new file mode 100644
index 0000000..a8967f2
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/AbstractRenameDetectionTestCase.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2022, 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.diff;
+
+import static org.junit.Assert.assertEquals;
+
+import org.eclipse.jgit.diff.DiffEntry.ChangeType;
+import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.AbbreviatedObjectId;
+import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Repository;
+import org.junit.Before;
+
+public abstract class AbstractRenameDetectionTestCase
+		extends RepositoryTestCase {
+
+	protected static final String PATH_A = "src/A";
+
+	protected static final String PATH_B = "src/B";
+
+	protected static final String PATH_H = "src/H";
+
+	protected static final String PATH_Q = "src/Q";
+
+	protected TestRepository<Repository> testDb;
+
+	@Override
+	@Before
+	public void setUp() throws Exception {
+		super.setUp();
+		testDb = new TestRepository<>(db);
+	}
+
+	protected ObjectId blob(String content) throws Exception {
+		return testDb.blob(content).copy();
+	}
+
+	protected static void assertRename(DiffEntry o, DiffEntry n, int score,
+			DiffEntry rename) {
+		assertEquals(ChangeType.RENAME, rename.getChangeType());
+
+		assertEquals(o.getOldPath(), rename.getOldPath());
+		assertEquals(n.getNewPath(), rename.getNewPath());
+
+		assertEquals(o.getOldMode(), rename.getOldMode());
+		assertEquals(n.getNewMode(), rename.getNewMode());
+
+		assertEquals(o.getOldId(), rename.getOldId());
+		assertEquals(n.getNewId(), rename.getNewId());
+
+		assertEquals(score, rename.getScore());
+	}
+
+	protected static void assertCopy(DiffEntry o, DiffEntry n, int score,
+			DiffEntry copy) {
+		assertEquals(ChangeType.COPY, copy.getChangeType());
+
+		assertEquals(o.getOldPath(), copy.getOldPath());
+		assertEquals(n.getNewPath(), copy.getNewPath());
+
+		assertEquals(o.getOldMode(), copy.getOldMode());
+		assertEquals(n.getNewMode(), copy.getNewMode());
+
+		assertEquals(o.getOldId(), copy.getOldId());
+		assertEquals(n.getNewId(), copy.getNewId());
+
+		assertEquals(score, copy.getScore());
+	}
+
+	protected static void assertAdd(String newName, ObjectId newId,
+			FileMode newMode, DiffEntry add) {
+		assertEquals(DiffEntry.DEV_NULL, add.oldPath);
+		assertEquals(DiffEntry.A_ZERO, add.oldId);
+		assertEquals(FileMode.MISSING, add.oldMode);
+		assertEquals(ChangeType.ADD, add.changeType);
+		assertEquals(newName, add.newPath);
+		assertEquals(AbbreviatedObjectId.fromObjectId(newId), add.newId);
+		assertEquals(newMode, add.newMode);
+	}
+
+	protected static void assertDelete(String oldName, ObjectId oldId,
+			FileMode oldMode, DiffEntry delete) {
+		assertEquals(DiffEntry.DEV_NULL, delete.newPath);
+		assertEquals(DiffEntry.A_ZERO, delete.newId);
+		assertEquals(FileMode.MISSING, delete.newMode);
+		assertEquals(ChangeType.DELETE, delete.changeType);
+		assertEquals(oldName, delete.oldPath);
+		assertEquals(AbbreviatedObjectId.fromObjectId(oldId), delete.oldId);
+		assertEquals(oldMode, delete.oldMode);
+	}
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/FilteredRenameDetectorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/FilteredRenameDetectorTest.java
new file mode 100644
index 0000000..bfda36d
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/FilteredRenameDetectorTest.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2022, Simeon Andreev 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.diff;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+
+import java.util.Arrays;
+import java.util.List;
+import org.eclipse.jgit.internal.diff.FilteredRenameDetector;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.treewalk.filter.PathFilter;
+import org.junit.Before;
+import org.junit.Test;
+
+public class FilteredRenameDetectorTest extends AbstractRenameDetectionTestCase {
+
+	private FilteredRenameDetector frd;
+
+	@Override
+	@Before
+	public void setUp() throws Exception {
+		super.setUp();
+		frd = new FilteredRenameDetector(db);
+	}
+
+	@Test
+	public void testExactRename() throws Exception {
+		ObjectId foo = blob("foo");
+		ObjectId bar = blob("bar");
+
+		DiffEntry a = DiffEntry.add(PATH_A, foo);
+		DiffEntry b = DiffEntry.delete(PATH_Q, foo);
+
+		DiffEntry c = DiffEntry.add(PATH_H, bar);
+		DiffEntry d = DiffEntry.delete(PATH_B, bar);
+
+		List<DiffEntry> changes = Arrays.asList(a, b, c, d);
+		PathFilter filter = PathFilter.create(PATH_A);
+		List<DiffEntry> entries = frd.compute(changes, filter);
+		assertEquals("Unexpected entries in: " + entries, 1, entries.size());
+		assertRename(b, a, 100, entries.get(0));
+	}
+
+	@Test
+	public void testExactRename_multipleFilters() throws Exception {
+		ObjectId foo = blob("foo");
+		ObjectId bar = blob("bar");
+
+		DiffEntry a = DiffEntry.add(PATH_A, foo);
+		DiffEntry b = DiffEntry.delete(PATH_Q, foo);
+
+		DiffEntry c = DiffEntry.add(PATH_H, bar);
+		DiffEntry d = DiffEntry.delete(PATH_B, bar);
+
+		List<DiffEntry> changes = Arrays.asList(a, b, c, d);
+		List<PathFilter> filters = Arrays.asList(PathFilter.create(PATH_A),
+				PathFilter.create(PATH_H));
+		List<DiffEntry> entries = frd.compute(changes, filters);
+		assertEquals("Unexpected entries in: " + entries, 2, entries.size());
+		assertRename(b, a, 100, entries.get(0));
+		assertRename(d, c, 100, entries.get(1));
+	}
+
+	@Test
+	public void testInexactRename() throws Exception {
+		ObjectId aId = blob("foo\nbar\nbaz\nblarg\n");
+		ObjectId bId = blob("foo\nbar\nbaz\nblah\n");
+		DiffEntry a = DiffEntry.add(PATH_A, aId);
+		DiffEntry b = DiffEntry.delete(PATH_Q, bId);
+
+		ObjectId cId = blob("some\nsort\nof\ntext\n");
+		ObjectId dId = blob("completely\nunrelated\ntext\n");
+		DiffEntry c = DiffEntry.add(PATH_B, cId);
+		DiffEntry d = DiffEntry.delete(PATH_H, dId);
+
+		List<DiffEntry> changes = Arrays.asList(a, b, c, d);
+		PathFilter filter = PathFilter.create(PATH_A);
+		List<DiffEntry> entries = frd.compute(changes, filter);
+		assertEquals("Unexpected entries: " + entries, 1, entries.size());
+		assertRename(b, a, 66, entries.get(0));
+	}
+
+	@Test
+	public void testInexactRename_multipleFilters() throws Exception {
+		ObjectId aId = blob("foo\nbar\nbaz\nblarg\n");
+		ObjectId bId = blob("foo\nbar\nbaz\nblah\n");
+		DiffEntry a = DiffEntry.add(PATH_A, aId);
+		DiffEntry b = DiffEntry.delete(PATH_Q, bId);
+
+		ObjectId cId = blob("some\nsort\nof\ntext\n");
+		ObjectId dId = blob("completely\nunrelated\ntext\n");
+		DiffEntry c = DiffEntry.add(PATH_B, cId);
+		DiffEntry d = DiffEntry.delete(PATH_H, dId);
+
+		List<DiffEntry> changes = Arrays.asList(a, b, c, d);
+		List<PathFilter> filters = Arrays.asList(PathFilter.create(PATH_A),
+				PathFilter.create(PATH_H));
+		List<DiffEntry> entries = frd.compute(changes, filters);
+		assertEquals("Unexpected entries: " + entries, 2, entries.size());
+		assertRename(b, a, 66, entries.get(0));
+		assertSame(d, entries.get(1));
+	}
+
+	@Test
+	public void testNoRenames() throws Exception {
+		ObjectId aId = blob("");
+		ObjectId bId = blob("blah1");
+		ObjectId cId = blob("");
+		ObjectId dId = blob("blah2");
+
+		DiffEntry a = DiffEntry.add(PATH_A, aId);
+		DiffEntry b = DiffEntry.delete(PATH_Q, bId);
+
+		DiffEntry c = DiffEntry.add(PATH_H, cId);
+		DiffEntry d = DiffEntry.delete(PATH_B, dId);
+
+		List<DiffEntry> changes = Arrays.asList(a, b, c, d);
+		PathFilter filter = PathFilter.create(PATH_A);
+		List<DiffEntry> entries = frd.compute(changes, filter);
+		assertEquals("Unexpected entries in: " + entries, 1, entries.size());
+		assertSame(a, entries.get(0));
+	}
+
+	@Test
+	public void testNoRenames_multipleFilters() throws Exception {
+		ObjectId aId = blob("");
+		ObjectId bId = blob("blah1");
+		ObjectId cId = blob("");
+		ObjectId dId = blob("blah2");
+
+		DiffEntry a = DiffEntry.add(PATH_A, aId);
+		DiffEntry b = DiffEntry.delete(PATH_Q, bId);
+
+		DiffEntry c = DiffEntry.add(PATH_H, cId);
+		DiffEntry d = DiffEntry.delete(PATH_B, dId);
+
+		List<DiffEntry> changes = Arrays.asList(a, b, c, d);
+		List<PathFilter> filters = Arrays.asList(PathFilter.create(PATH_A),
+				PathFilter.create(PATH_H));
+		List<DiffEntry> entries = frd.compute(changes, filters);
+		assertEquals("Unexpected entries in: " + entries, 2, entries.size());
+		assertSame(a, entries.get(0));
+		assertSame(c, entries.get(1));
+	}
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/RenameDetectorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/RenameDetectorTest.java
index 5edb60c..ad560e3 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/RenameDetectorTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/RenameDetectorTest.java
@@ -18,31 +18,20 @@
 import java.util.Arrays;
 import java.util.List;
 
-import org.eclipse.jgit.diff.DiffEntry.ChangeType;
-import org.eclipse.jgit.junit.RepositoryTestCase;
-import org.eclipse.jgit.junit.TestRepository;
 import org.eclipse.jgit.lib.AbbreviatedObjectId;
 import org.eclipse.jgit.lib.FileMode;
 import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.Repository;
 import org.junit.Before;
 import org.junit.Test;
 
-public class RenameDetectorTest extends RepositoryTestCase {
-	private static final String PATH_A = "src/A";
-	private static final String PATH_B = "src/B";
-	private static final String PATH_H = "src/H";
-	private static final String PATH_Q = "src/Q";
+public class RenameDetectorTest extends AbstractRenameDetectionTestCase {
 
 	private RenameDetector rd;
 
-	private TestRepository<Repository> testDb;
-
 	@Override
 	@Before
 	public void setUp() throws Exception {
 		super.setUp();
-		testDb = new TestRepository<>(db);
 		rd = new RenameDetector(db);
 	}
 
@@ -675,62 +664,4 @@ public void testRenameLimit() throws Exception {
 		assertSame(c, entries.get(2));
 		assertSame(d, entries.get(3));
 	}
-
-	private ObjectId blob(String content) throws Exception {
-		return testDb.blob(content).copy();
-	}
-
-	private static void assertRename(DiffEntry o, DiffEntry n, int score,
-			DiffEntry rename) {
-		assertEquals(ChangeType.RENAME, rename.getChangeType());
-
-		assertEquals(o.getOldPath(), rename.getOldPath());
-		assertEquals(n.getNewPath(), rename.getNewPath());
-
-		assertEquals(o.getOldMode(), rename.getOldMode());
-		assertEquals(n.getNewMode(), rename.getNewMode());
-
-		assertEquals(o.getOldId(), rename.getOldId());
-		assertEquals(n.getNewId(), rename.getNewId());
-
-		assertEquals(score, rename.getScore());
-	}
-
-	private static void assertCopy(DiffEntry o, DiffEntry n, int score,
-			DiffEntry copy) {
-		assertEquals(ChangeType.COPY, copy.getChangeType());
-
-		assertEquals(o.getOldPath(), copy.getOldPath());
-		assertEquals(n.getNewPath(), copy.getNewPath());
-
-		assertEquals(o.getOldMode(), copy.getOldMode());
-		assertEquals(n.getNewMode(), copy.getNewMode());
-
-		assertEquals(o.getOldId(), copy.getOldId());
-		assertEquals(n.getNewId(), copy.getNewId());
-
-		assertEquals(score, copy.getScore());
-	}
-
-	private static void assertAdd(String newName, ObjectId newId,
-			FileMode newMode, DiffEntry add) {
-		assertEquals(DiffEntry.DEV_NULL, add.oldPath);
-		assertEquals(DiffEntry.A_ZERO, add.oldId);
-		assertEquals(FileMode.MISSING, add.oldMode);
-		assertEquals(ChangeType.ADD, add.changeType);
-		assertEquals(newName, add.newPath);
-		assertEquals(AbbreviatedObjectId.fromObjectId(newId), add.newId);
-		assertEquals(newMode, add.newMode);
-	}
-
-	private static void assertDelete(String oldName, ObjectId oldId,
-			FileMode oldMode, DiffEntry delete) {
-		assertEquals(DiffEntry.DEV_NULL, delete.newPath);
-		assertEquals(DiffEntry.A_ZERO, delete.newId);
-		assertEquals(FileMode.MISSING, delete.newMode);
-		assertEquals(ChangeType.DELETE, delete.changeType);
-		assertEquals(oldName, delete.oldPath);
-		assertEquals(AbbreviatedObjectId.fromObjectId(oldId), delete.oldId);
-		assertEquals(oldMode, delete.oldMode);
-	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java
index 3e6d13a..ca6f2e1 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java
@@ -13,6 +13,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.junit.Assume.assumeTrue;
@@ -32,6 +33,7 @@
 import org.eclipse.jgit.api.errors.GitAPIException;
 import org.eclipse.jgit.api.errors.InvalidRefNameException;
 import org.eclipse.jgit.api.errors.InvalidRemoteException;
+import org.eclipse.jgit.gitrepo.RepoCommand.ManifestErrorException;
 import org.eclipse.jgit.gitrepo.RepoCommand.RemoteFile;
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.junit.JGitTestUtil;
@@ -1337,6 +1339,28 @@ public void testTwoPathUseTheSameName() throws Exception {
 		}
 	}
 
+	@Test
+	public void testInvalidPath() throws Exception {
+		Repository remoteDb = createBareRepository();
+		Repository tempDb = createWorkRepository();
+
+		StringBuilder xmlContent = new StringBuilder();
+		xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
+				.append("<manifest>")
+				.append("<remote name=\"remote1\" fetch=\".\" />")
+				.append("<default revision=\"master\" remote=\"remote1\" />")
+				.append("<project path=\".\" ").append("name=\"")
+				.append(defaultUri).append("\" />").append("</manifest>");
+		JGitTestUtil.writeTrashFile(tempDb, "manifest.xml",
+				xmlContent.toString());
+
+		RepoCommand command = new RepoCommand(remoteDb);
+		command.setPath(
+				tempDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
+				.setURI(rootUri).setRecommendShallow(true);
+		assertThrows(ManifestErrorException.class, () -> command.call());
+	}
+
 	private void resolveRelativeUris() {
 		// Find the longest common prefix ends with "/" as rootUri.
 		defaultUri = defaultDb.getDirectory().toURI().toString();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/CGitIgnoreTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/CGitIgnoreTest.java
index ae3f051..083e6cd 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/CGitIgnoreTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/CGitIgnoreTest.java
@@ -13,6 +13,7 @@
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 
 import java.io.BufferedInputStream;
 import java.io.BufferedReader;
@@ -20,6 +21,7 @@
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStreamReader;
+import java.nio.file.Files;
 import java.util.LinkedHashSet;
 import java.util.Set;
 
@@ -354,4 +356,84 @@ public void testNegationAllExceptJavaInSrcAndExceptChildDirInSrc()
 		writeTrashFile("src/.gitignore", "*\n!*.java\n!*/");
 		assertSameAsCGit();
 	}
+
+	@Test
+	public void testMultipleEntriesIgnored() throws Exception {
+		createFiles("dir/a");
+		writeTrashFile(".gitignore", "!dir/a\ndir/a");
+		assertSameAsCGit();
+	}
+
+	@Test
+	public void testMultipleEntriesNotIgnored() throws Exception {
+		createFiles("dir/a");
+		writeTrashFile(".gitignore", "dir/a\n!dir/a");
+		assertSameAsCGit("dir/a");
+	}
+
+	@Test
+	public void testInfoExcludes() throws Exception {
+		createFiles("dir/a", "dir/b");
+		File gitDir = db.getDirectory();
+		File info = new File(gitDir, "info");
+		assertTrue(info.mkdirs());
+		File infoExclude = new File(info, "exclude");
+		Files.writeString(infoExclude.toPath(), "dir/a");
+		assertSameAsCGit("dir/b");
+	}
+
+	@Test
+	public void testInfoExcludesPrecedence() throws Exception {
+		createFiles("dir/a", "dir/b");
+		writeTrashFile(".gitignore", "!dir/a");
+		File gitDir = db.getDirectory();
+		File info = new File(gitDir, "info");
+		assertTrue(info.mkdirs());
+		File infoExclude = new File(info, "exclude");
+		Files.writeString(infoExclude.toPath(), "dir/a");
+		assertSameAsCGit("dir/a", "dir/b");
+	}
+
+	@Test
+	public void testCoreExcludes() throws Exception {
+		createFiles("dir/a", "dir/b");
+		writeTrashFile(".fake_git_ignore", "dir/a");
+		assertSameAsCGit("dir/b");
+	}
+
+	@Test
+	public void testInfoCoreExcludes() throws Exception {
+		createFiles("dir/a", "dir/b");
+		File gitDir = db.getDirectory();
+		File info = new File(gitDir, "info");
+		assertTrue(info.mkdirs());
+		File infoExclude = new File(info, "exclude");
+		Files.writeString(infoExclude.toPath(), "!a");
+		writeTrashFile(".fake_git_ignore", "dir/a");
+		assertSameAsCGit("dir/b");
+	}
+
+	@Test
+	public void testInfoCoreExcludesPrecedence() throws Exception {
+		createFiles("dir/a", "dir/b");
+		File gitDir = db.getDirectory();
+		File info = new File(gitDir, "info");
+		assertTrue(info.mkdirs());
+		File infoExclude = new File(info, "exclude");
+		Files.writeString(infoExclude.toPath(), "!dir/a");
+		writeTrashFile(".fake_git_ignore", "dir/a");
+		assertSameAsCGit("dir/a", "dir/b");
+	}
+
+	@Test
+	public void testInfoCoreExcludesPrecedence2() throws Exception {
+		createFiles("dir/a", "dir/b");
+		File gitDir = db.getDirectory();
+		File info = new File(gitDir, "info");
+		assertTrue(info.mkdirs());
+		File infoExclude = new File(info, "exclude");
+		Files.writeString(infoExclude.toPath(), "dir/a");
+		writeTrashFile(".fake_git_ignore", "!dir/a");
+		assertSameAsCGit("dir/b");
+	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergerTest.java
index cbacaed..dc119c9 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergerTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergerTest.java
@@ -1002,6 +1002,39 @@ public void checkContentMergeConflict_noTree(MergeStrategy strategy)
 		}
 	}
 
+	@Theory
+	public void fileBecomesDir_noTree(MergeStrategy strategy)
+			throws Exception {
+		Git git = Git.wrap(db);
+
+		writeTrashFile("file", "1\n2\n3");
+		writeTrashFile("side", "1\n2\n3");
+		git.add().addFilepattern("file").addFilepattern("side").call();
+		RevCommit first = git.commit().setMessage("base").call();
+
+		writeTrashFile("side", "our changed");
+		RevCommit ours = git.commit().setAll(true)
+				.setMessage("ours").call();
+
+		git.checkout().setCreateBranch(true).setStartPoint(first)
+				.setName("theirs").call();
+		deleteTrashFile("file");
+		writeTrashFile("file/file", "in subdir");
+		git.add().addFilepattern("file/file").call();
+
+		RevCommit theirs = git.commit().setAll(true)
+				.setMessage("theirs").call();
+
+		// Exercise inCore flavor of the merge.
+		try (ObjectInserter ins = db.newObjectInserter()) {
+			ResolveMerger merger =
+					(ResolveMerger) strategy.newMerger(ins, db.getConfig());
+			boolean success = merger.merge(ours, theirs);
+			assertTrue(success);
+			assertTrue(merger.getModifiedFiles().isEmpty());
+		}
+	}
+
 	/**
 	 * Merging after criss-cross merges. In this case we merge together two
 	 * commits which have two equally good common ancestors
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SymlinkMergeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SymlinkMergeTest.java
new file mode 100644
index 0000000..3cdc8da
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SymlinkMergeTest.java
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2022 Thomas Wolf <twolf@apache.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
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.merge;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.MergeResult;
+import org.eclipse.jgit.api.MergeResult.MergeStatus;
+import org.eclipse.jgit.api.ResetCommand.ResetType;
+import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.ConfigConstants;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.StoredConfig;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+/**
+ * Tests for merges involving symlinks.
+ */
+@RunWith(Parameterized.class)
+public class SymlinkMergeTest extends RepositoryTestCase {
+
+	@Parameters(name = "target={0}, core.symlinks={1}")
+	public static Object[][] parameters() {
+		return new Object[][] {
+			{ Target.NONE, Boolean.TRUE },
+			{ Target.FILE, Boolean.TRUE },
+			{ Target.DIRECTORY, Boolean.TRUE },
+			{ Target.NONE, Boolean.FALSE },
+			{ Target.FILE, Boolean.FALSE },
+			{ Target.DIRECTORY, Boolean.FALSE },
+		};
+	}
+
+	public enum Target {
+		NONE, FILE, DIRECTORY
+	}
+
+	@Parameter(0)
+	public Target target;
+
+	@Parameter(1)
+	public boolean useSymLinks;
+
+	private void setTargets() throws IOException {
+		switch (target) {
+		case DIRECTORY:
+			assertTrue(new File(trash, "target").mkdir());
+			assertTrue(new File(trash, "target1").mkdir());
+			assertTrue(new File(trash, "target2").mkdir());
+			break;
+		case FILE:
+			writeTrashFile("target", "t");
+			writeTrashFile("target1", "t1");
+			writeTrashFile("target2", "t2");
+			break;
+		default:
+			break;
+		}
+	}
+
+	private void checkTargets() throws IOException {
+		File t = new File(trash, "target");
+		File t1 = new File(trash, "target1");
+		File t2 = new File(trash, "target2");
+		switch (target) {
+		case DIRECTORY:
+			assertTrue(t.isDirectory());
+			assertTrue(t1.isDirectory());
+			assertTrue(t2.isDirectory());
+			break;
+		case FILE:
+			checkFile(t, "t");
+			checkFile(t1, "t1");
+			checkFile(t2, "t2");
+			break;
+		default:
+			assertFalse(Files.exists(t.toPath(), LinkOption.NOFOLLOW_LINKS));
+			assertFalse(Files.exists(t1.toPath(), LinkOption.NOFOLLOW_LINKS));
+			assertFalse(Files.exists(t2.toPath(), LinkOption.NOFOLLOW_LINKS));
+			break;
+		}
+	}
+
+	private void assertSymLink(File link, String content) throws Exception {
+		if (useSymLinks) {
+			assertTrue(Files.isSymbolicLink(link.toPath()));
+			assertEquals(content, db.getFS().readSymLink(link));
+		} else {
+			assertFalse(Files.isSymbolicLink(link.toPath()));
+			assertTrue(link.isFile());
+			checkFile(link, content);
+		}
+	}
+
+	// Link/link conflict: C git records the conflict but leaves the link in the
+	// working tree unchanged.
+
+	@Test
+	public void mergeWithSymlinkConflict() throws Exception {
+		assumeTrue(db.getFS().supportsSymlinks() || !useSymLinks);
+		StoredConfig config = db.getConfig();
+		config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
+				ConfigConstants.CONFIG_KEY_SYMLINKS, useSymLinks);
+		config.save();
+		try (TestRepository<Repository> repo = new TestRepository<>(db)) {
+			db.incrementOpen();
+			// Create the links directly in the git repo, then use a hard reset
+			// to get them into the workspace. This enables us to run these
+			// tests also with core.symLinks = false.
+			RevCommit base = repo
+					.commit(repo.tree(repo.link("link", repo.blob("target"))));
+			RevCommit side = repo.commit(
+					repo.tree(repo.link("link", repo.blob("target1"))), base);
+			RevCommit head = repo.commit(
+					repo.tree(repo.link("link", repo.blob("target2"))), base);
+			try (Git git = new Git(db)) {
+				setTargets();
+				git.reset().setMode(ResetType.HARD).setRef(head.name()).call();
+				File link = new File(trash, "link");
+				assertSymLink(link, "target2");
+				MergeResult result = git.merge().include(side)
+						.setMessage("merged").call();
+				assertEquals(MergeStatus.CONFLICTING, result.getMergeStatus());
+				// Link should be unmodified
+				assertSymLink(link, "target2");
+				checkTargets();
+				assertEquals("[link, mode:120000, stage:1, content:target]"
+						+ "[link, mode:120000, stage:2, content:target2]"
+						+ "[link, mode:120000, stage:3, content:target1]",
+						indexState(CONTENT));
+			}
+		}
+	}
+
+	// In file/link conflicts, C git never does a content merge. It records the
+	// stages in the index, and always puts the file into the workspace.
+
+	@Test
+	public void mergeWithFileSymlinkConflict() throws Exception {
+		assumeTrue(db.getFS().supportsSymlinks() || !useSymLinks);
+		StoredConfig config = db.getConfig();
+		config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
+				ConfigConstants.CONFIG_KEY_SYMLINKS, useSymLinks);
+		config.save();
+		try (TestRepository<Repository> repo = new TestRepository<>(db)) {
+			db.incrementOpen();
+			RevCommit base = repo.commit(repo.tree());
+			RevCommit side = repo.commit(
+					repo.tree(repo.link("link", repo.blob("target1"))), base);
+			RevCommit head = repo.commit(
+					repo.tree(repo.file("link", repo.blob("not a link"))),
+					base);
+			try (Git git = new Git(db)) {
+				setTargets();
+				git.reset().setMode(ResetType.HARD).setRef(head.name()).call();
+				File link = new File(trash, "link");
+				assertFalse(Files.isSymbolicLink(link.toPath()));
+				checkFile(link, "not a link");
+				MergeResult result = git.merge().include(side)
+						.setMessage("merged").call();
+				assertEquals(MergeStatus.CONFLICTING, result.getMergeStatus());
+				// File should be unmodified
+				assertFalse(Files.isSymbolicLink(link.toPath()));
+				checkFile(link, "not a link");
+				checkTargets();
+				assertEquals("[link, mode:100644, stage:2, content:not a link]"
+						+ "[link, mode:120000, stage:3, content:target1]",
+						indexState(CONTENT));
+			}
+		}
+	}
+
+	@Test
+	public void mergeWithSymlinkFileConflict() throws Exception {
+		assumeTrue(db.getFS().supportsSymlinks() || !useSymLinks);
+		StoredConfig config = db.getConfig();
+		config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
+				ConfigConstants.CONFIG_KEY_SYMLINKS, useSymLinks);
+		config.save();
+		try (TestRepository<Repository> repo = new TestRepository<>(db)) {
+			db.incrementOpen();
+			RevCommit base = repo.commit(repo.tree());
+			RevCommit side = repo.commit(
+					repo.tree(repo.file("link", repo.blob("not a link"))),
+					base);
+			RevCommit head = repo.commit(
+					repo.tree(repo.link("link", repo.blob("target2"))), base);
+			try (Git git = new Git(db)) {
+				setTargets();
+				git.reset().setMode(ResetType.HARD).setRef(head.name()).call();
+				File link = new File(trash, "link");
+				assertSymLink(link, "target2");
+				MergeResult result = git.merge().include(side)
+						.setMessage("merged").call();
+				assertEquals(MergeStatus.CONFLICTING, result.getMergeStatus());
+				// Should now be a file!
+				assertFalse(Files.isSymbolicLink(link.toPath()));
+				checkFile(link, "not a link");
+				checkTargets();
+				assertEquals("[link, mode:120000, stage:2, content:target2]"
+						+ "[link, mode:100644, stage:3, content:not a link]",
+						indexState(CONTENT));
+			}
+		}
+	}
+
+	// In Delete/modify conflicts with the non-deleted side a link, C git puts
+	// the link into the working tree.
+
+	@Test
+	public void mergeWithSymlinkDeleteModify() throws Exception {
+		assumeTrue(db.getFS().supportsSymlinks() || !useSymLinks);
+		StoredConfig config = db.getConfig();
+		config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
+				ConfigConstants.CONFIG_KEY_SYMLINKS, useSymLinks);
+		config.save();
+		try (TestRepository<Repository> repo = new TestRepository<>(db)) {
+			db.incrementOpen();
+			RevCommit base = repo
+					.commit(repo.tree(repo.link("link", repo.blob("target"))));
+			RevCommit side = repo.commit(
+					repo.tree(repo.link("link", repo.blob("target1"))), base);
+			RevCommit head = repo.commit(repo.tree(), base);
+			try (Git git = new Git(db)) {
+				setTargets();
+				git.reset().setMode(ResetType.HARD).setRef(head.name()).call();
+				File link = new File(trash, "link");
+				assertFalse(
+						Files.exists(link.toPath(), LinkOption.NOFOLLOW_LINKS));
+				MergeResult result = git.merge().include(side)
+						.setMessage("merged").call();
+				assertEquals(MergeStatus.CONFLICTING, result.getMergeStatus());
+				// Link should have the content from side
+				assertSymLink(link, "target1");
+				checkTargets();
+				assertEquals("[link, mode:120000, stage:1, content:target]"
+						+ "[link, mode:120000, stage:3, content:target1]",
+						indexState(CONTENT));
+			}
+		}
+	}
+
+	@Test
+	public void mergeWithSymlinkModifyDelete() throws Exception {
+		assumeTrue(db.getFS().supportsSymlinks() || !useSymLinks);
+		StoredConfig config = db.getConfig();
+		config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
+				ConfigConstants.CONFIG_KEY_SYMLINKS, useSymLinks);
+		config.save();
+		try (TestRepository<Repository> repo = new TestRepository<>(db)) {
+			db.incrementOpen();
+			RevCommit base = repo
+					.commit(repo.tree(repo.link("link", repo.blob("target"))));
+			RevCommit side = repo.commit(repo.tree(), base);
+			RevCommit head = repo.commit(
+					repo.tree(repo.link("link", repo.blob("target2"))), base);
+			try (Git git = new Git(db)) {
+				setTargets();
+				git.reset().setMode(ResetType.HARD).setRef(head.name()).call();
+				File link = new File(trash, "link");
+				assertSymLink(link, "target2");
+				MergeResult result = git.merge().include(side)
+						.setMessage("merged").call();
+				assertEquals(MergeStatus.CONFLICTING, result.getMergeStatus());
+				// Link should be unmodified
+				assertSymLink(link, "target2");
+				checkTargets();
+				assertEquals("[link, mode:120000, stage:1, content:target]"
+						+ "[link, mode:120000, stage:2, content:target2]",
+						indexState(CONTENT));
+			}
+		}
+	}
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevCommitParseTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevCommitParseTest.java
index 0e4c05d..82af34d 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevCommitParseTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevCommitParseTest.java
@@ -78,7 +78,7 @@ public void testParse_NoParents() throws Exception {
 
 		c = new RevCommit(id("9473095c4cb2f12aefe1db8a355fe3fafba42f67"));
 		assertNull(c.getTree());
-		assertNull(c.parents);
+		assertNull(c.getParents());
 
 		try (RevWalk rw = new RevWalk(db)) {
 			c.parseCanonical(rw, body.toString().getBytes(UTF_8));
@@ -86,23 +86,26 @@ public void testParse_NoParents() throws Exception {
 			assertEquals(treeId, c.getTree().getId());
 			assertSame(rw.lookupTree(treeId), c.getTree());
 		}
-		assertNotNull(c.parents);
-		assertEquals(0, c.parents.length);
+		assertNotNull(c.getParents());
+		assertEquals(0, c.getParentCount());
 		assertEquals("", c.getFullMessage());
 
 		final PersonIdent cAuthor = c.getAuthorIdent();
 		assertNotNull(cAuthor);
 		assertEquals(authorName, cAuthor.getName());
 		assertEquals(authorEmail, cAuthor.getEmailAddress());
-		assertEquals((long)authorTime * 1000, cAuthor.getWhen().getTime());
-		assertEquals(TimeZone.getTimeZone("GMT" + authorTimeZone), cAuthor.getTimeZone());
+		assertEquals((long) authorTime * 1000, cAuthor.getWhen().getTime());
+		assertEquals(TimeZone.getTimeZone("GMT" + authorTimeZone),
+				cAuthor.getTimeZone());
 
 		final PersonIdent cCommitter = c.getCommitterIdent();
 		assertNotNull(cCommitter);
 		assertEquals(committerName, cCommitter.getName());
 		assertEquals(committerEmail, cCommitter.getEmailAddress());
-		assertEquals((long)committerTime * 1000, cCommitter.getWhen().getTime());
-		assertEquals(TimeZone.getTimeZone("GMT" + committerTimeZone), cCommitter.getTimeZone());
+		assertEquals((long) committerTime * 1000,
+				cCommitter.getWhen().getTime());
+		assertEquals(TimeZone.getTimeZone("GMT" + committerTimeZone),
+				cCommitter.getTimeZone());
 	}
 
 	private RevCommit create(String msg) throws Exception {
@@ -149,16 +152,22 @@ public void testParse_incompleteAuthorAndCommitter() throws Exception {
 		try (RevWalk rw = new RevWalk(db)) {
 			c.parseCanonical(rw, b.toString().getBytes(UTF_8));
 		}
-		assertEquals(new PersonIdent("", "a_u_thor@example.com", 1218123387000l, 7), c.getAuthorIdent());
-		assertEquals(new PersonIdent("", "", 1218123390000l, -5), c.getCommitterIdent());
+		assertEquals(
+				new PersonIdent("", "a_u_thor@example.com", 1218123387000l, 7),
+				c.getAuthorIdent());
+		assertEquals(new PersonIdent("", "", 1218123390000l, -5),
+				c.getCommitterIdent());
 	}
 
 	@Test
 	public void testParse_implicit_UTF8_encoded() throws Exception {
 		final ByteArrayOutputStream b = new ByteArrayOutputStream();
-		b.write("tree 9788669ad918b6fcce64af8882fc9a81cb6aba67\n".getBytes(UTF_8));
-		b.write("author F\u00f6r fattare <a_u_thor@example.com> 1218123387 +0700\n".getBytes(UTF_8));
-		b.write("committer C O. Miter <c@example.com> 1218123390 -0500\n".getBytes(UTF_8));
+		b.write("tree 9788669ad918b6fcce64af8882fc9a81cb6aba67\n"
+				.getBytes(UTF_8));
+		b.write("author F\u00f6r fattare <a_u_thor@example.com> 1218123387 +0700\n"
+				.getBytes(UTF_8));
+		b.write("committer C O. Miter <c@example.com> 1218123390 -0500\n"
+				.getBytes(UTF_8));
 		b.write("\n".getBytes(UTF_8));
 		b.write("Sm\u00f6rg\u00e5sbord\n".getBytes(UTF_8));
 		b.write("\n".getBytes(UTF_8));
@@ -171,15 +180,19 @@ public void testParse_implicit_UTF8_encoded() throws Exception {
 		assertSame(UTF_8, c.getEncoding());
 		assertEquals("F\u00f6r fattare", c.getAuthorIdent().getName());
 		assertEquals("Sm\u00f6rg\u00e5sbord", c.getShortMessage());
-		assertEquals("Sm\u00f6rg\u00e5sbord\n\n\u304d\u308c\u3044\n", c.getFullMessage());
+		assertEquals("Sm\u00f6rg\u00e5sbord\n\n\u304d\u308c\u3044\n",
+				c.getFullMessage());
 	}
 
 	@Test
 	public void testParse_implicit_mixed_encoded() throws Exception {
 		final ByteArrayOutputStream b = new ByteArrayOutputStream();
-		b.write("tree 9788669ad918b6fcce64af8882fc9a81cb6aba67\n".getBytes(UTF_8));
-		b.write("author F\u00f6r fattare <a_u_thor@example.com> 1218123387 +0700\n".getBytes(ISO_8859_1));
-		b.write("committer C O. Miter <c@example.com> 1218123390 -0500\n".getBytes(UTF_8));
+		b.write("tree 9788669ad918b6fcce64af8882fc9a81cb6aba67\n"
+				.getBytes(UTF_8));
+		b.write("author F\u00f6r fattare <a_u_thor@example.com> 1218123387 +0700\n"
+				.getBytes(ISO_8859_1));
+		b.write("committer C O. Miter <c@example.com> 1218123390 -0500\n"
+				.getBytes(UTF_8));
 		b.write("\n".getBytes(UTF_8));
 		b.write("Sm\u00f6rg\u00e5sbord\n".getBytes(UTF_8));
 		b.write("\n".getBytes(UTF_8));
@@ -192,7 +205,8 @@ public void testParse_implicit_mixed_encoded() throws Exception {
 		assertSame(UTF_8, c.getEncoding());
 		assertEquals("F\u00f6r fattare", c.getAuthorIdent().getName());
 		assertEquals("Sm\u00f6rg\u00e5sbord", c.getShortMessage());
-		assertEquals("Sm\u00f6rg\u00e5sbord\n\n\u304d\u308c\u3044\n", c.getFullMessage());
+		assertEquals("Sm\u00f6rg\u00e5sbord\n\n\u304d\u308c\u3044\n",
+				c.getFullMessage());
 	}
 
 	/**
@@ -203,9 +217,12 @@ public void testParse_implicit_mixed_encoded() throws Exception {
 	@Test
 	public void testParse_explicit_encoded() throws Exception {
 		final ByteArrayOutputStream b = new ByteArrayOutputStream();
-		b.write("tree 9788669ad918b6fcce64af8882fc9a81cb6aba67\n".getBytes("EUC-JP"));
-		b.write("author F\u00f6r fattare <a_u_thor@example.com> 1218123387 +0700\n".getBytes("EUC-JP"));
-		b.write("committer C O. Miter <c@example.com> 1218123390 -0500\n".getBytes("EUC-JP"));
+		b.write("tree 9788669ad918b6fcce64af8882fc9a81cb6aba67\n"
+				.getBytes("EUC-JP"));
+		b.write("author F\u00f6r fattare <a_u_thor@example.com> 1218123387 +0700\n"
+				.getBytes("EUC-JP"));
+		b.write("committer C O. Miter <c@example.com> 1218123390 -0500\n"
+				.getBytes("EUC-JP"));
 		b.write("encoding euc_JP\n".getBytes("EUC-JP"));
 		b.write("\n".getBytes("EUC-JP"));
 		b.write("\u304d\u308c\u3044\n".getBytes("EUC-JP"));
@@ -235,9 +252,12 @@ public void testParse_explicit_encoded() throws Exception {
 	@Test
 	public void testParse_explicit_bad_encoded() throws Exception {
 		final ByteArrayOutputStream b = new ByteArrayOutputStream();
-		b.write("tree 9788669ad918b6fcce64af8882fc9a81cb6aba67\n".getBytes(UTF_8));
-		b.write("author F\u00f6r fattare <a_u_thor@example.com> 1218123387 +0700\n".getBytes(ISO_8859_1));
-		b.write("committer C O. Miter <c@example.com> 1218123390 -0500\n".getBytes(UTF_8));
+		b.write("tree 9788669ad918b6fcce64af8882fc9a81cb6aba67\n"
+				.getBytes(UTF_8));
+		b.write("author F\u00f6r fattare <a_u_thor@example.com> 1218123387 +0700\n"
+				.getBytes(ISO_8859_1));
+		b.write("committer C O. Miter <c@example.com> 1218123390 -0500\n"
+				.getBytes(UTF_8));
 		b.write("encoding EUC-JP\n".getBytes(UTF_8));
 		b.write("\n".getBytes(UTF_8));
 		b.write("\u304d\u308c\u3044\n".getBytes(UTF_8));
@@ -256,21 +276,25 @@ public void testParse_explicit_bad_encoded() throws Exception {
 	}
 
 	/**
-	 * This is a twisted case too, but show what we expect here. We can revise the
-	 * expectations provided this case is updated.
+	 * This is a twisted case too, but show what we expect here. We can revise
+	 * the expectations provided this case is updated.
 	 *
 	 * What happens here is that an encoding us given, but data is not encoded
-	 * that way (and we can detect it), so we try other encodings. Here data could
-	 * actually be decoded in the stated encoding, but we override using UTF-8.
+	 * that way (and we can detect it), so we try other encodings. Here data
+	 * could actually be decoded in the stated encoding, but we override using
+	 * UTF-8.
 	 *
 	 * @throws Exception
 	 */
 	@Test
 	public void testParse_explicit_bad_encoded2() throws Exception {
 		final ByteArrayOutputStream b = new ByteArrayOutputStream();
-		b.write("tree 9788669ad918b6fcce64af8882fc9a81cb6aba67\n".getBytes(UTF_8));
-		b.write("author F\u00f6r fattare <a_u_thor@example.com> 1218123387 +0700\n".getBytes(UTF_8));
-		b.write("committer C O. Miter <c@example.com> 1218123390 -0500\n".getBytes(UTF_8));
+		b.write("tree 9788669ad918b6fcce64af8882fc9a81cb6aba67\n"
+				.getBytes(UTF_8));
+		b.write("author F\u00f6r fattare <a_u_thor@example.com> 1218123387 +0700\n"
+				.getBytes(UTF_8));
+		b.write("committer C O. Miter <c@example.com> 1218123390 -0500\n"
+				.getBytes(UTF_8));
 		b.write("encoding ISO-8859-1\n".getBytes(UTF_8));
 		b.write("\n".getBytes(UTF_8));
 		b.write("\u304d\u308c\u3044\n".getBytes(UTF_8));
@@ -319,9 +343,11 @@ public void testParse_incorrectUtf8Name() throws Exception {
 	@Test
 	public void testParse_illegalEncoding() throws Exception {
 		ByteArrayOutputStream b = new ByteArrayOutputStream();
-		b.write("tree 9788669ad918b6fcce64af8882fc9a81cb6aba67\n".getBytes(UTF_8));
+		b.write("tree 9788669ad918b6fcce64af8882fc9a81cb6aba67\n"
+				.getBytes(UTF_8));
 		b.write("author au <a@example.com> 1218123387 +0700\n".getBytes(UTF_8));
-		b.write("committer co <c@example.com> 1218123390 -0500\n".getBytes(UTF_8));
+		b.write("committer co <c@example.com> 1218123390 -0500\n"
+				.getBytes(UTF_8));
 		b.write("encoding utf-8logoutputencoding=gbk\n".getBytes(UTF_8));
 		b.write("\n".getBytes(UTF_8));
 		b.write("message\n".getBytes(UTF_8));
@@ -348,9 +374,11 @@ public void testParse_illegalEncoding() throws Exception {
 	@Test
 	public void testParse_unsupportedEncoding() throws Exception {
 		ByteArrayOutputStream b = new ByteArrayOutputStream();
-		b.write("tree 9788669ad918b6fcce64af8882fc9a81cb6aba67\n".getBytes(UTF_8));
+		b.write("tree 9788669ad918b6fcce64af8882fc9a81cb6aba67\n"
+				.getBytes(UTF_8));
 		b.write("author au <a@example.com> 1218123387 +0700\n".getBytes(UTF_8));
-		b.write("committer co <c@example.com> 1218123390 -0500\n".getBytes(UTF_8));
+		b.write("committer co <c@example.com> 1218123390 -0500\n"
+				.getBytes(UTF_8));
 		b.write("encoding it_IT.UTF8\n".getBytes(UTF_8));
 		b.write("\n".getBytes(UTF_8));
 		b.write("message\n".getBytes(UTF_8));
@@ -474,21 +502,18 @@ private static ObjectId id(String str) {
 
 	@Test
 	public void testParse_gpgSig() throws Exception {
-		String commit = "tree e3a1035abd2b319bb01e57d69b0ba6cab289297e\n" +
-		"parent 54e895b87c0768d2317a2b17062e3ad9f76a8105\n" +
-		"committer A U Thor <author@xample.com 1528968566 +0200\n" +
-		"gpgsig -----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-----\n" +
-		"some other header\n\n" +
-		"commit message";
+		String commit = "tree e3a1035abd2b319bb01e57d69b0ba6cab289297e\n"
+				+ "parent 54e895b87c0768d2317a2b17062e3ad9f76a8105\n"
+				+ "committer A U Thor <author@xample.com 1528968566 +0200\n"
+				+ "gpgsig -----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-----\n"
+				+ "some other header\n\n" + "commit message";
 
 		final RevCommit c;
 		c = new RevCommit(id("9473095c4cb2f12aefe1db8a355fe3fafba42f67"));
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevCommitWithOverriddenParentTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevCommitWithOverriddenParentTest.java
new file mode 100644
index 0000000..b06f9fc
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevCommitWithOverriddenParentTest.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2022, 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.revwalk;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+
+import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
+import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.revwalk.filter.RevFilter;
+import org.junit.Before;
+import org.junit.Test;
+
+public class RevCommitWithOverriddenParentTest {
+	private TestRepository<InMemoryRepository> tr;
+
+	private RevWalk rw;
+
+	@Before
+	public void setUp() throws Exception {
+		tr = new TestRepository<>(
+				new InMemoryRepository(new DfsRepositoryDescription("test")));
+		rw = tr.getRevWalk();
+	}
+
+	@Test
+	public void testParseBody() throws Exception {
+		RevCommit a = tr.commit().add("a", "foo").create();
+		RevCommit b = tr.commit().parent(a).add("b", "bar").create();
+		RevCommit c = tr.commit().parent(b).message("commit3").add("a", "foo'")
+				.create();
+
+		RevCommit cBar = new RevCommit(c.getId()) {
+			@Override
+			public int getParentCount() {
+				return 1;
+			}
+
+			@Override
+			public RevCommit getParent(int nth) {
+				return a;
+			}
+
+			@Override
+			public RevCommit[] getParents() {
+				return new RevCommit[] { a };
+			}
+		};
+
+		rw.parseBody(cBar);
+		assertEquals(a, cBar.getParents()[0]);
+		assertEquals("commit3", cBar.getFullMessage());
+		assertEquals("foo'", blobAsString(cBar, "a"));
+	}
+
+	@Test
+	public void testParseHeader() throws Exception {
+		RevCommit a = tr.commit().add("a", "foo").create();
+		RevCommit b = tr.commit().parent(a).add("b", "bar").create();
+		RevCommit c = tr.commit().parent(b).message("commit3").add("a", "foo'")
+				.create();
+
+		RevCommit cBar = new RevCommit(c.getId()) {
+			@Override
+			public int getParentCount() {
+				return 1;
+			}
+
+			@Override
+			public RevCommit getParent(int nth) {
+				return a;
+			}
+
+			@Override
+			public RevCommit[] getParents() {
+				return new RevCommit[] { a };
+			}
+		};
+
+		RevCommit parsed = rw.parseCommit(cBar.getId());
+		rw.parseHeaders(cBar);
+
+		assertEquals(c.getId(), parsed.getId());
+		assertEquals(parsed.getTree(), cBar.getTree());
+		assertEquals(parsed.getCommitTime(), cBar.getCommitTime());
+		assertEquals(parsed.getAuthorIdent(), cBar.getAuthorIdent());
+	}
+
+	@Test
+	public void testFilter() throws Exception {
+		RevCommit a = tr.commit().add("a", "foo").create();
+		RevCommit b = tr.commit().parent(a).add("b", "bar").create();
+		RevCommit c = tr.commit().parent(b).message("commit3").add("a", "foo'")
+				.create();
+
+		RevCommit cBar = new RevCommit(c.getId()) {
+			@Override
+			public int getParentCount() {
+				return 1;
+			}
+
+			@Override
+			public RevCommit getParent(int nth) {
+				return a;
+			}
+
+			@Override
+			public RevCommit[] getParents() {
+				return new RevCommit[] { a };
+			}
+		};
+
+		rw.setRevFilter(RevFilter.ALL);
+		rw.markStart(cBar);
+		assertSame(cBar, rw.next());
+		assertSame(a, rw.next());
+		assertNull(rw.next());
+	}
+
+	@Test
+	public void testFlag() throws Exception {
+		RevCommit root = tr.commit().add("todelete", "to be deleted").create();
+		RevCommit orig = tr.commit().parent(root).rm("todelete")
+				.add("foo", "foo contents").add("bar", "bar contents")
+				.add("dir/baz", "baz contents").create();
+
+		RevCommit commitOrigBar = new RevCommit(orig.getId()) {
+			@Override
+			public int getParentCount() {
+				return 1;
+			}
+
+			@Override
+			public RevCommit getParent(int nth) {
+				return root;
+			}
+
+			@Override
+			public RevCommit[] getParents() {
+				return new RevCommit[] { root };
+			}
+		};
+
+		assertEquals(RevObject.PARSED, orig.flags);
+		assertEquals(0, commitOrigBar.flags);
+		commitOrigBar.parseBody(rw);
+		assertEquals(RevObject.PARSED, commitOrigBar.flags);
+	}
+
+	private String blobAsString(AnyObjectId treeish, String path)
+			throws Exception {
+		RevObject obj = tr.get(rw.parseTree(treeish), path);
+		assertSame(RevBlob.class, obj.getClass());
+		ObjectLoader loader = rw.getObjectReader().open(obj);
+		return new String(loader.getCachedBytes(), UTF_8);
+	}
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkCullTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkCullTest.java
index 722b2d2..7b0e2b2 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkCullTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkCullTest.java
@@ -75,6 +75,6 @@ public void testProperlyCullAllAncestors_LongHistory() throws Exception {
 		// We should have aborted before we got back so far that "a"
 		// would be parsed. Thus, its parents shouldn't be allocated.
 		//
-		assertNull(a2.parents);
+		assertNull(a2.getParents());
 	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV0ParserTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV0ParserTest.java
index c0db83a..b2a4af3 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV0ParserTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV0ParserTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018, Google LLC. and others
+ * Copyright (C) 2018, 2022 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
@@ -15,6 +15,7 @@
 import static org.eclipse.jgit.lib.Constants.OBJ_TREE;
 import static org.eclipse.jgit.transport.ObjectIdMatcher.hasOnlyObjectIds;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.contains;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
@@ -133,6 +134,42 @@ public void testRecvWantsDeepen()
 	}
 
 	@Test
+	public void testRecvWantsDeepenSince()
+			throws PackProtocolException, IOException {
+		PacketLineIn pckIn = formatAsPacketLine(
+				"want 4624442d68ee402a94364191085b77137618633e\n",
+				"want f900c8326a43303685c46b279b9f70411bff1a4b\n",
+				"deepen-since 1652773020\n",
+				PacketLineIn.end());
+		ProtocolV0Parser parser = new ProtocolV0Parser(defaultConfig());
+		FetchV0Request request = parser.recvWants(pckIn);
+		assertTrue(request.getClientCapabilities().isEmpty());
+		assertEquals(1652773020, request.getDeepenSince());
+		assertThat(request.getWantIds(),
+				   hasOnlyObjectIds("4624442d68ee402a94364191085b77137618633e",
+									"f900c8326a43303685c46b279b9f70411bff1a4b"));
+	}
+
+	@Test
+	public void testRecvWantsDeepenNots()
+			throws PackProtocolException, IOException {
+		PacketLineIn pckIn = formatAsPacketLine(
+				"want 4624442d68ee402a94364191085b77137618633e\n",
+				"want f900c8326a43303685c46b279b9f70411bff1a4b\n",
+				"deepen-not 856d5138d7269a483efe276d4a6b5c25b4fbb1a4\n",
+				"deepen-not heads/refs/test\n",
+				PacketLineIn.end());
+		ProtocolV0Parser parser = new ProtocolV0Parser(defaultConfig());
+		FetchV0Request request = parser.recvWants(pckIn);
+		assertTrue(request.getClientCapabilities().isEmpty());
+		assertThat(request.getDeepenNots(), contains("856d5138d7269a483efe276d4a6b5c25b4fbb1a4",
+													 "heads/refs/test"));
+		assertThat(request.getWantIds(),
+				   hasOnlyObjectIds("4624442d68ee402a94364191085b77137618633e",
+									"f900c8326a43303685c46b279b9f70411bff1a4b"));
+	}
+
+	@Test
 	public void testRecvWantsShallow()
 			throws PackProtocolException, IOException {
 		PacketLineIn pckIn = formatAsPacketLine(
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV2ParserTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV2ParserTest.java
index 837bdce..167b5b7 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV2ParserTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV2ParserTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018, Google LLC. and others
+ * Copyright (C) 2018, 2022 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
@@ -152,7 +152,7 @@ public void testFetchWithShallow_deepen() throws IOException {
 		assertThat(request.getClientShallowCommits(),
 				hasOnlyObjectIds("28274d02c489f4c7e68153056e9061a46f62d7a0",
 						"145e683b229dcab9d0e2ccb01b386f9ecc17d29d"));
-		assertTrue(request.getDeepenNotRefs().isEmpty());
+		assertTrue(request.getDeepenNots().isEmpty());
 		assertEquals(15, request.getDepth());
 		assertTrue(request.getClientCapabilities()
 				.contains(GitProtocolConstants.OPTION_DEEPEN_RELATIVE));
@@ -171,7 +171,7 @@ public void testFetchWithShallow_deepenNot() throws IOException {
 		assertThat(request.getClientShallowCommits(),
 				hasOnlyObjectIds("28274d02c489f4c7e68153056e9061a46f62d7a0",
 						"145e683b229dcab9d0e2ccb01b386f9ecc17d29d"));
-		assertThat(request.getDeepenNotRefs(),
+		assertThat(request.getDeepenNots(),
 				hasItems("a08595f76159b09d57553e37a5123f1091bb13e7"));
 	}
 
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java
index 0d49cd3..e463e90 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java
@@ -303,12 +303,16 @@ public void testIsModifiedSymlinkAsFile() throws Exception {
 		DirCacheEntry dce = db.readDirCache().getEntry("symlink");
 		dce.setFileMode(FileMode.SYMLINK);
 		try (ObjectReader objectReader = db.newObjectReader()) {
-			DirCacheCheckout.checkoutEntry(db, dce, objectReader, false, null);
+			WorkingTreeOptions options = db.getConfig()
+					.get(WorkingTreeOptions.KEY);
+			DirCacheCheckout.checkoutEntry(db, dce, objectReader, false, null,
+					options);
 
 			FileTreeIterator fti = new FileTreeIterator(trash, db.getFS(),
-					db.getConfig().get(WorkingTreeOptions.KEY));
-			while (!fti.getEntryPathString().equals("symlink"))
+					options);
+			while (!fti.getEntryPathString().equals("symlink")) {
 				fti.next(1);
+			}
 			assertFalse(fti.isModified(dce, false, objectReader));
 		}
 	}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/NameConflictTreeWalkTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/NameConflictTreeWalkTest.java
index 36a96b0..f03163d 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/NameConflictTreeWalkTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/NameConflictTreeWalkTest.java
@@ -138,6 +138,54 @@ public void testDF_GapByOne() throws Exception {
 	}
 
 	@Test
+	public void testDF_specialFileNames() throws Exception {
+		final DirCache tree0 = db.readDirCache();
+		final DirCache tree1 = db.readDirCache();
+		final DirCache tree2 = db.readDirCache();
+		{
+			final DirCacheBuilder b0 = tree0.builder();
+			final DirCacheBuilder b1 = tree1.builder();
+			final DirCacheBuilder b2 = tree2.builder();
+
+			b0.add(createEntry("gradle.properties", REGULAR_FILE));
+			b0.add(createEntry("gradle/nested_file.txt", REGULAR_FILE));
+
+			b1.add(createEntry("gradle.properties", REGULAR_FILE));
+
+			b2.add(createEntry("gradle", REGULAR_FILE));
+			b2.add(createEntry("gradle.properties", REGULAR_FILE));
+
+			b0.finish();
+			b1.finish();
+			b2.finish();
+			assertEquals(2, tree0.getEntryCount());
+			assertEquals(1, tree1.getEntryCount());
+			assertEquals(2, tree2.getEntryCount());
+		}
+
+		try (NameConflictTreeWalk tw = new NameConflictTreeWalk(db)) {
+			tw.addTree(new DirCacheIterator(tree0));
+			tw.addTree(new DirCacheIterator(tree1));
+			tw.addTree(new DirCacheIterator(tree2));
+
+			assertModes("gradle", TREE, MISSING, REGULAR_FILE, tw);
+			assertTrue(tw.isSubtree());
+			assertTrue(tw.isDirectoryFileConflict());
+			tw.enterSubtree();
+			assertModes("gradle/nested_file.txt", REGULAR_FILE, MISSING,
+					MISSING, tw);
+			assertFalse(tw.isSubtree());
+			// isDirectoryFileConflict is true, because the conflict is detected
+			// on parent.
+			assertTrue(tw.isDirectoryFileConflict());
+			assertModes("gradle.properties", REGULAR_FILE, REGULAR_FILE,
+					REGULAR_FILE, tw);
+			assertFalse(tw.isSubtree());
+			assertFalse(tw.isDirectoryFileConflict());
+		}
+	}
+
+	@Test
 	public void testDF_SkipsSeenSubtree() throws Exception {
 		final DirCache tree0 = db.readDirCache();
 		final DirCache tree1 = db.readDirCache();
@@ -218,11 +266,29 @@ public void testDF_DetectConflict() throws Exception {
 		}
 	}
 
-	private static void assertModes(final String path, final FileMode mode0,
-			final FileMode mode1, final TreeWalk tw) throws Exception {
+	private static void assertModes(String path, FileMode mode0, FileMode mode1,
+			TreeWalk tw) throws Exception {
 		assertTrue("has " + path, tw.next());
 		assertEquals(path, tw.getPathString());
 		assertEquals(mode0, tw.getFileMode(0));
 		assertEquals(mode1, tw.getFileMode(1));
 	}
+
+	private static void assertModes(String path, FileMode mode0, FileMode mode1,
+			FileMode mode2, TreeWalk tw) throws Exception {
+		assertTrue("has " + path, tw.next());
+		assertEquals(path, tw.getPathString());
+		if (tw.getFileMode(0) != FileMode.MISSING) {
+			assertEquals(path, TreeWalk.pathOf(tw.trees[0]));
+		}
+		if (tw.getFileMode(1) != FileMode.MISSING) {
+			assertEquals(path, TreeWalk.pathOf(tw.trees[1]));
+		}
+		if (tw.getFileMode(2) != FileMode.MISSING) {
+			assertEquals(path, TreeWalk.pathOf(tw.trees[2]));
+		}
+		assertEquals(mode0, tw.getFileMode(0));
+		assertEquals(mode1, tw.getFileMode(1));
+		assertEquals(mode2, tw.getFileMode(2));
+	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/HookTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/HookTest.java
index 33ed360..1231aef 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/HookTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/HookTest.java
@@ -9,6 +9,7 @@
  */
 package org.eclipse.jgit.util;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.fail;
@@ -77,7 +78,7 @@ public void testFailedCommitMsgHookBlocksCommit() throws Exception {
 					"Rejected by \"commit-msg\" hook.\nstderr\n",
 					e.getMessage());
 			assertEquals("unexpected output from commit-msg hook", "test\n",
-					out.toString());
+					out.toString(UTF_8));
 		}
 	}
 
@@ -95,7 +96,7 @@ public void testCommitMsgHookReceivesCorrectParameter() throws Exception {
 		git.commit().setMessage("commit")
 				.setHookOutputStream(new PrintStream(out)).call();
 		assertEquals(".git/COMMIT_EDITMSG\n",
-				out.toString("UTF-8"));
+				out.toString(UTF_8));
 	}
 
 	@Test
@@ -129,9 +130,9 @@ public void testPostCommitRunHook() throws Exception {
 				new PrintStream(out), new PrintStream(err), "stdin");
 
 		assertEquals("unexpected hook output", "test arg1 arg2\nstdin\n",
-				out.toString("UTF-8"));
+				out.toString(UTF_8));
 		assertEquals("unexpected output on stderr stream", "stderr\n",
-				err.toString("UTF-8"));
+				err.toString(UTF_8));
 		assertEquals("unexpected exit code", 0, res.getExitCode());
 		assertEquals("unexpected process status", ProcessResult.Status.OK,
 				res.getStatus());
@@ -160,7 +161,7 @@ public void testAllCommitHooks() throws Exception {
 		}
 		assertEquals("unexpected hook output",
 				"test pre-commit\ntest commit-msg .git/COMMIT_EDITMSG\ntest post-commit\n",
-				out.toString("UTF-8"));
+				out.toString(UTF_8));
 	}
 
 	@Test
@@ -181,9 +182,9 @@ public void testRunHook() throws Exception {
 		assertEquals("unexpected hook output",
 				"test arg1 arg2\nstdin\n" + db.getDirectory().getAbsolutePath()
 						+ '\n' + db.getWorkTree().getAbsolutePath() + '\n',
-				out.toString("UTF-8"));
+				out.toString(UTF_8));
 		assertEquals("unexpected output on stderr stream", "stderr\n",
-				err.toString("UTF-8"));
+				err.toString(UTF_8));
 		assertEquals("unexpected exit code", 0, res.getExitCode());
 		assertEquals("unexpected process status", ProcessResult.Status.OK,
 				res.getStatus());
@@ -214,9 +215,9 @@ public void testRunHookHooksPathRelative() throws Exception {
 					"test arg1 arg2\nstdin\n"
 							+ db.getDirectory().getAbsolutePath() + '\n'
 							+ db.getWorkTree().getAbsolutePath() + '\n',
-					out.toString("UTF-8"));
+					out.toString(UTF_8));
 			assertEquals("unexpected output on stderr stream", "stderr\n",
-					err.toString("UTF-8"));
+					err.toString(UTF_8));
 			assertEquals("unexpected exit code", 0, res.getExitCode());
 			assertEquals("unexpected process status", ProcessResult.Status.OK,
 					res.getStatus());
@@ -249,9 +250,9 @@ public void testRunHookHooksPathAbsolute() throws Exception {
 					"test arg1 arg2\nstdin\n"
 							+ db.getDirectory().getAbsolutePath() + '\n'
 							+ db.getWorkTree().getAbsolutePath() + '\n',
-					out.toString("UTF-8"));
+					out.toString(UTF_8));
 			assertEquals("unexpected output on stderr stream", "stderr\n",
-					err.toString("UTF-8"));
+					err.toString(UTF_8));
 			assertEquals("unexpected exit code", 0, res.getExitCode());
 			assertEquals("unexpected process status", ProcessResult.Status.OK,
 					res.getStatus());
@@ -281,9 +282,9 @@ public void testHookPathWithBlank() throws Exception {
 					"test arg1 arg2\nstdin\n"
 							+ db.getDirectory().getAbsolutePath() + '\n'
 							+ db.getWorkTree().getAbsolutePath() + '\n',
-					out.toString("UTF-8"));
+					out.toString(UTF_8));
 			assertEquals("unexpected output on stderr stream", "stderr\n",
-					err.toString("UTF-8"));
+					err.toString(UTF_8));
 			assertEquals("unexpected exit code", 0, res.getExitCode());
 			assertEquals("unexpected process status", ProcessResult.Status.OK,
 					res.getStatus());
@@ -310,7 +311,7 @@ public void testFailedPreCommitHookBlockCommit() throws Exception {
 					"Rejected by \"pre-commit\" hook.\nstderr\n",
 					e.getMessage());
 			assertEquals("unexpected output from pre-commit hook", "test\n",
-					out.toString());
+					out.toString(UTF_8));
 		}
 	}
 
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/PathsTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/PathsTest.java
index c697688..2fae909 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/PathsTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/PathsTest.java
@@ -13,7 +13,9 @@
 import static org.eclipse.jgit.util.Paths.compare;
 import static org.eclipse.jgit.util.Paths.compareSameName;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.FileMode;
@@ -32,6 +34,23 @@ public void testStripTrailingSeparator() {
 	}
 
 	@Test
+	public void testPrefix() {
+		assertTrue(Paths.isEqualOrPrefix("a", "a"));
+		assertTrue(Paths.isEqualOrPrefix("a", "a/b"));
+		assertTrue(Paths.isEqualOrPrefix("a", "a/a.txt"));
+		assertFalse(Paths.isEqualOrPrefix("a", "ab"));
+		assertFalse(Paths.isEqualOrPrefix("a", "a.txt"));
+		assertFalse(Paths.isEqualOrPrefix("a", "b/a.txt"));
+		assertFalse(Paths.isEqualOrPrefix("a", "b/a"));
+		assertFalse(Paths.isEqualOrPrefix("a", "ab/a.txt"));
+		assertFalse(Paths.isEqualOrPrefix("", "a"));
+		assertTrue(Paths.isEqualOrPrefix("", ""));
+		assertTrue(Paths.isEqualOrPrefix("a/b", "a/b"));
+		assertTrue(Paths.isEqualOrPrefix("a/b", "a/b/c"));
+		assertFalse(Paths.isEqualOrPrefix("a/b", "a/bc"));
+	}
+
+	@Test
 	public void testPathCompare() {
 		byte[] a = Constants.encode("afoo/bar.c");
 		byte[] b = Constants.encode("bfoo/bar.c");
diff --git a/org.eclipse.jgit.ui/META-INF/MANIFEST.MF b/org.eclipse.jgit.ui/META-INF/MANIFEST.MF
index 17f38b9..3552ffb 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: 6.2.1.qualifier
+Bundle-Version: 6.3.1.qualifier
 Bundle-Vendor: %Bundle-Vendor
 Bundle-RequiredExecutionEnvironment: JavaSE-11
-Export-Package: org.eclipse.jgit.awtui;version="6.2.1"
-Import-Package: org.eclipse.jgit.errors;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.lib;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.nls;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.revplot;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.revwalk;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.transport;version="[6.2.1,6.3.0)",
- org.eclipse.jgit.util;version="[6.2.1,6.3.0)"
+Export-Package: org.eclipse.jgit.awtui;version="6.3.1"
+Import-Package: org.eclipse.jgit.errors;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.lib;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.nls;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.revplot;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.revwalk;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.transport;version="[6.3.1,6.4.0)",
+ org.eclipse.jgit.util;version="[6.3.1,6.4.0)"
diff --git a/org.eclipse.jgit.ui/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.ui/META-INF/SOURCE-MANIFEST.MF
index 5753bf9..147b1e4 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: 6.2.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.ui;version="6.2.1.qualifier";roots="."
+Bundle-Version: 6.3.1.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.ui;version="6.3.1.qualifier";roots="."
diff --git a/org.eclipse.jgit.ui/pom.xml b/org.eclipse.jgit.ui/pom.xml
index ed940c6..fce6d4d 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>6.2.1-SNAPSHOT</version>
+    <version>6.3.1-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.ui</artifactId>
diff --git a/org.eclipse.jgit/.settings/.api_filters b/org.eclipse.jgit/.settings/.api_filters
index 3c3a22f..f45bd07 100644
--- a/org.eclipse.jgit/.settings/.api_filters
+++ b/org.eclipse.jgit/.settings/.api_filters
@@ -54,6 +54,64 @@
             </message_arguments>
         </filter>
     </resource>
+    <resource path="src/org/eclipse/jgit/merge/ResolveMerger.java" type="org.eclipse.jgit.merge.ResolveMerger">
+        <filter id="336658481">
+            <message_arguments>
+                <message_argument value="org.eclipse.jgit.merge.ResolveMerger"/>
+                <message_argument value="workTreeUpdater"/>
+            </message_arguments>
+        </filter>
+        <filter id="338755678">
+            <message_arguments>
+                <message_argument value="org.eclipse.jgit.merge.ResolveMerger"/>
+                <message_argument value="builder"/>
+            </message_arguments>
+        </filter>
+        <filter id="338755678">
+            <message_arguments>
+                <message_argument value="org.eclipse.jgit.merge.ResolveMerger"/>
+                <message_argument value="implicitDirCache"/>
+            </message_arguments>
+        </filter>
+        <filter id="338755678">
+            <message_arguments>
+                <message_argument value="org.eclipse.jgit.merge.ResolveMerger"/>
+                <message_argument value="toBeCheckedOut"/>
+            </message_arguments>
+        </filter>
+        <filter id="338755678">
+            <message_arguments>
+                <message_argument value="org.eclipse.jgit.merge.ResolveMerger"/>
+                <message_argument value="toBeDeleted"/>
+            </message_arguments>
+        </filter>
+        <filter id="338755678">
+            <message_arguments>
+                <message_argument value="org.eclipse.jgit.merge.ResolveMerger"/>
+                <message_argument value="workingTreeOptions"/>
+            </message_arguments>
+        </filter>
+        <filter id="338792546">
+            <message_arguments>
+                <message_argument value="org.eclipse.jgit.merge.ResolveMerger"/>
+                <message_argument value="addCheckoutMetadata(Map&lt;String,DirCacheCheckout.CheckoutMetadata&gt;, String, Attributes)"/>
+            </message_arguments>
+        </filter>
+        <filter id="338792546">
+            <message_arguments>
+                <message_argument value="org.eclipse.jgit.merge.ResolveMerger"/>
+                <message_argument value="cleanUp()"/>
+            </message_arguments>
+        </filter>
+    </resource>
+    <resource path="src/org/eclipse/jgit/merge/ResolveMerger.java" type="org.eclipse.jgit.merge.ResolveMerger$WorkTreeUpdater">
+        <filter id="1142947843">
+            <message_arguments>
+                <message_argument value="6.3.1"/>
+                <message_argument value="WorkTreeUpdater"/>
+            </message_arguments>
+        </filter>
+    </resource>
     <resource path="src/org/eclipse/jgit/storage/pack/PackConfig.java" type="org.eclipse.jgit.storage.pack.PackConfig">
         <filter id="336658481">
             <message_arguments>
@@ -96,6 +154,13 @@
             </message_arguments>
         </filter>
     </resource>
+    <resource path="src/org/eclipse/jgit/util/Paths.java" type="org.eclipse.jgit.util.Paths">
+        <filter id="337768515">
+            <message_arguments>
+                <message_argument value="org.eclipse.jgit.util.Paths"/>
+            </message_arguments>
+        </filter>
+    </resource>
     <resource path="src/org/eclipse/jgit/util/sha1/SHA1.java" type="org.eclipse.jgit.util.sha1.SHA1">
         <filter id="337764418">
             <message_arguments>
diff --git a/org.eclipse.jgit/BUILD b/org.eclipse.jgit/BUILD
index 04873b0..e806e7d 100644
--- a/org.eclipse.jgit/BUILD
+++ b/org.eclipse.jgit/BUILD
@@ -23,6 +23,12 @@
         "//lib:javaewah",
         "//lib:slf4j-api",
     ],
+    javacopts = [
+        "-Xep:ReferenceEquality:OFF",
+        "-Xep:StringEquality:OFF",
+        "-Xep:TypeParameterUnusedInFormals:OFF",
+        "-Xep:DefaultCharset:OFF",
+    ]
 )
 
 genrule(
diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF
index 1b34912..307a801 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: 6.2.1.qualifier
+Bundle-Version: 6.3.1.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %Bundle-Vendor
 Eclipse-ExtensibleAPI: true
-Export-Package: org.eclipse.jgit.annotations;version="6.2.1",
- org.eclipse.jgit.api;version="6.2.1";
+Export-Package: org.eclipse.jgit.annotations;version="6.3.1",
+ org.eclipse.jgit.api;version="6.3.1";
   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="6.2.1";
+ org.eclipse.jgit.api.errors;version="6.3.1";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.errors",
- org.eclipse.jgit.attributes;version="6.2.1";
+ org.eclipse.jgit.attributes;version="6.3.1";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.treewalk",
- org.eclipse.jgit.blame;version="6.2.1";
+ org.eclipse.jgit.blame;version="6.3.1";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.revwalk,
    org.eclipse.jgit.treewalk.filter,
    org.eclipse.jgit.diff",
- org.eclipse.jgit.diff;version="6.2.1";
+ org.eclipse.jgit.diff;version="6.3.1";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.attributes,
    org.eclipse.jgit.revwalk,
@@ -42,49 +42,51 @@
    org.eclipse.jgit.treewalk.filter,
    org.eclipse.jgit.treewalk,
    org.eclipse.jgit.util",
- org.eclipse.jgit.dircache;version="6.2.1";
+ org.eclipse.jgit.dircache;version="6.3.1";
   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="6.2.1";
+ org.eclipse.jgit.errors;version="6.3.1";
   uses:="org.eclipse.jgit.transport,
    org.eclipse.jgit.dircache,
    org.eclipse.jgit.lib,
    org.eclipse.jgit.internal.storage.pack",
- org.eclipse.jgit.events;version="6.2.1";
+ org.eclipse.jgit.events;version="6.3.1";
   uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.fnmatch;version="6.2.1",
- org.eclipse.jgit.gitrepo;version="6.2.1";
+ org.eclipse.jgit.fnmatch;version="6.3.1",
+ org.eclipse.jgit.gitrepo;version="6.3.1";
   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="6.2.1";x-internal:=true,
- org.eclipse.jgit.hooks;version="6.2.1";uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.ignore;version="6.2.1",
- org.eclipse.jgit.ignore.internal;version="6.2.1";
+ org.eclipse.jgit.gitrepo.internal;version="6.3.1";x-internal:=true,
+ org.eclipse.jgit.hooks;version="6.3.1";uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.ignore;version="6.3.1",
+ org.eclipse.jgit.ignore.internal;version="6.3.1";
   x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal;version="6.2.1";
+ org.eclipse.jgit.internal;version="6.3.1";
   x-friends:="org.eclipse.jgit.test,
    org.eclipse.jgit.http.test",
- org.eclipse.jgit.internal.diffmergetool;version="6.2.1";
+ org.eclipse.jgit.internal.diff;version="6.3.1";
+  x-friends:="org.eclipse.jgit.test",
+ org.eclipse.jgit.internal.diffmergetool;version="6.3.1";
   x-friends:="org.eclipse.jgit.test,
    org.eclipse.jgit.pgm.test,
    org.eclipse.jgit.pgm,
    org.eclipse.egit.ui",
- org.eclipse.jgit.internal.fsck;version="6.2.1";
+ org.eclipse.jgit.internal.fsck;version="6.3.1";
   x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal.revwalk;version="6.2.1";
+ org.eclipse.jgit.internal.revwalk;version="6.3.1";
   x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal.storage.dfs;version="6.2.1";
+ org.eclipse.jgit.internal.storage.dfs;version="6.3.1";
   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="6.2.1";
+ org.eclipse.jgit.internal.storage.file;version="6.3.1";
   x-friends:="org.eclipse.jgit.test,
    org.eclipse.jgit.junit,
    org.eclipse.jgit.junit.http,
@@ -93,32 +95,32 @@
    org.eclipse.jgit.pgm,
    org.eclipse.jgit.pgm.test,
    org.eclipse.jgit.ssh.apache",
- org.eclipse.jgit.internal.storage.io;version="6.2.1";
+ org.eclipse.jgit.internal.storage.io;version="6.3.1";
   x-friends:="org.eclipse.jgit.junit,
    org.eclipse.jgit.test,
    org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.storage.pack;version="6.2.1";
+ org.eclipse.jgit.internal.storage.pack;version="6.3.1";
   x-friends:="org.eclipse.jgit.junit,
    org.eclipse.jgit.test,
    org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.storage.reftable;version="6.2.1";
+ org.eclipse.jgit.internal.storage.reftable;version="6.3.1";
   x-friends:="org.eclipse.jgit.http.test,
    org.eclipse.jgit.junit,
    org.eclipse.jgit.test,
    org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.submodule;version="6.2.1";x-internal:=true,
- org.eclipse.jgit.internal.transport.connectivity;version="6.2.1";
+ org.eclipse.jgit.internal.submodule;version="6.3.1";x-internal:=true,
+ org.eclipse.jgit.internal.transport.connectivity;version="6.3.1";
   x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal.transport.http;version="6.2.1";
+ org.eclipse.jgit.internal.transport.http;version="6.3.1";
   x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal.transport.parser;version="6.2.1";
+ org.eclipse.jgit.internal.transport.parser;version="6.3.1";
   x-friends:="org.eclipse.jgit.http.server,
    org.eclipse.jgit.test",
- org.eclipse.jgit.internal.transport.ssh;version="6.2.1";
+ org.eclipse.jgit.internal.transport.ssh;version="6.3.1";
   x-friends:="org.eclipse.jgit.ssh.apache,
    org.eclipse.jgit.ssh.jsch,
    org.eclipse.jgit.test",
- org.eclipse.jgit.lib;version="6.2.1";
+ org.eclipse.jgit.lib;version="6.3.1";
   uses:="org.eclipse.jgit.transport,
    org.eclipse.jgit.util.sha1,
    org.eclipse.jgit.dircache,
@@ -132,12 +134,12 @@
    org.eclipse.jgit.util,
    org.eclipse.jgit.submodule,
    org.eclipse.jgit.util.time",
- org.eclipse.jgit.lib.internal;version="6.2.1";
+ org.eclipse.jgit.lib.internal;version="6.3.1";
   x-friends:="org.eclipse.jgit.test,
    org.eclipse.jgit.pgm,
    org.eclipse.egit.ui",
- org.eclipse.jgit.logging;version="6.2.1",
- org.eclipse.jgit.merge;version="6.2.1";
+ org.eclipse.jgit.logging;version="6.3.1",
+ org.eclipse.jgit.merge;version="6.3.1";
   uses:="org.eclipse.jgit.dircache,
    org.eclipse.jgit.lib,
    org.eclipse.jgit.revwalk,
@@ -146,40 +148,40 @@
    org.eclipse.jgit.util,
    org.eclipse.jgit.api,
    org.eclipse.jgit.attributes",
- org.eclipse.jgit.nls;version="6.2.1",
- org.eclipse.jgit.notes;version="6.2.1";
+ org.eclipse.jgit.nls;version="6.3.1",
+ org.eclipse.jgit.notes;version="6.3.1";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.revwalk,
    org.eclipse.jgit.treewalk,
    org.eclipse.jgit.merge",
- org.eclipse.jgit.patch;version="6.2.1";
+ org.eclipse.jgit.patch;version="6.3.1";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.diff",
- org.eclipse.jgit.revplot;version="6.2.1";
+ org.eclipse.jgit.revplot;version="6.3.1";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.revwalk",
- org.eclipse.jgit.revwalk;version="6.2.1";
+ org.eclipse.jgit.revwalk;version="6.3.1";
   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="6.2.1";
+ org.eclipse.jgit.revwalk.filter;version="6.3.1";
   uses:="org.eclipse.jgit.revwalk,
    org.eclipse.jgit.lib,
    org.eclipse.jgit.util",
- org.eclipse.jgit.storage.file;version="6.2.1";
+ org.eclipse.jgit.storage.file;version="6.3.1";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.util",
- org.eclipse.jgit.storage.pack;version="6.2.1";
+ org.eclipse.jgit.storage.pack;version="6.3.1";
   uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.submodule;version="6.2.1";
+ org.eclipse.jgit.submodule;version="6.3.1";
   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="6.2.1";
+ org.eclipse.jgit.transport;version="6.3.1";
   uses:="javax.crypto,
    org.eclipse.jgit.util.io,
    org.eclipse.jgit.lib,
@@ -192,21 +194,21 @@
    org.eclipse.jgit.transport.resolver,
    org.eclipse.jgit.storage.pack,
    org.eclipse.jgit.errors",
- org.eclipse.jgit.transport.http;version="6.2.1";
+ org.eclipse.jgit.transport.http;version="6.3.1";
   uses:="javax.net.ssl",
- org.eclipse.jgit.transport.resolver;version="6.2.1";
+ org.eclipse.jgit.transport.resolver;version="6.3.1";
   uses:="org.eclipse.jgit.transport,
    org.eclipse.jgit.lib",
- org.eclipse.jgit.treewalk;version="6.2.1";
+ org.eclipse.jgit.treewalk;version="6.3.1";
   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="6.2.1";
+ org.eclipse.jgit.treewalk.filter;version="6.3.1";
   uses:="org.eclipse.jgit.treewalk",
- org.eclipse.jgit.util;version="6.2.1";
+ org.eclipse.jgit.util;version="6.3.1";
   uses:="org.eclipse.jgit.transport,
    org.eclipse.jgit.hooks,
    org.eclipse.jgit.revwalk,
@@ -219,12 +221,12 @@
    org.eclipse.jgit.treewalk,
    javax.net.ssl,
    org.eclipse.jgit.util.time",
- org.eclipse.jgit.util.io;version="6.2.1";
+ org.eclipse.jgit.util.io;version="6.3.1";
   uses:="org.eclipse.jgit.attributes,
    org.eclipse.jgit.lib,
    org.eclipse.jgit.treewalk",
- org.eclipse.jgit.util.sha1;version="6.2.1",
- org.eclipse.jgit.util.time;version="6.2.1"
+ org.eclipse.jgit.util.sha1;version="6.3.1",
+ org.eclipse.jgit.util.time;version="6.3.1"
 Bundle-RequiredExecutionEnvironment: JavaSE-11
 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 4066aea..43e88e5 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: 6.2.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit;version="6.2.1.qualifier";roots="."
+Bundle-Version: 6.3.1.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit;version="6.3.1.qualifier";roots="."
diff --git a/org.eclipse.jgit/pom.xml b/org.eclipse.jgit/pom.xml
index e223644..7e6bc0a 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>6.2.1-SNAPSHOT</version>
+    <version>6.3.1-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 84bffef..fedb7f5 100644
--- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
+++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
@@ -38,7 +38,7 @@
 badObjectType=Bad object type: {0}
 badRef=Bad ref: {0}: {1}
 badSectionEntry=Bad section entry: {0}
-badShallowLine=Bad shallow line: {0}
+badShallowLine=Shallow file ''{0}'' has bad line: {1}
 bareRepositoryNoWorkdirAndIndex=Bare Repository has neither a working tree, nor an index
 base85invalidChar=Invalid base-85 character: 0x{0}
 base85length=Base-85 encoded data must have a length that is a multiple of 5
@@ -238,6 +238,8 @@
 deleteRequiresZeroNewId=Delete requires new ID to be zero
 deleteTagUnexpectedResult=Delete tag returned unexpected result {0}
 deletingNotSupported=Deleting {0} not supported.
+depthMustBeAt1=Depth must be >= 1
+depthWithUnshallow=Depth and unshallow can\'t be used together
 destinationIsNotAWildcard=Destination is not a wildcard.
 detachedHeadDetected=HEAD is detached
 diffToolNotGivenError=No diff tool provided and no defaults configured.
@@ -521,6 +523,7 @@
 nothingToFetch=Nothing to fetch.
 nothingToPush=Nothing to push.
 notMergedExceptionMessage=Branch was not deleted as it has not been merged yet; use the force option to delete it anyway
+notShallowedUnshallow=The server sent a unshallow for a commit that wasn''t marked as shallow: {0}
 noXMLParserAvailable=No XML parser available.
 objectAtHasBadZlibStream=Object at {0} in {1} has bad zlib stream
 objectIsCorrupt=Object {0} is corrupt: {1}
@@ -590,6 +593,7 @@
 pushOptionsNotSupported=Push options not supported; received {0}
 rawLogMessageDoesNotParseAsLogEntry=Raw log message does not parse as log entry
 readConfigFailed=Reading config file ''{0}'' failed
+readShallowFailed=Reading shallow file ''{0}'' failed
 readFileStoreAttributesFailed=Reading FileStore attributes from user config failed
 readerIsRequired=Reader is required
 readingObjectsFromLocalRepositoryFailed=reading objects from local repository failed: {0}
@@ -665,6 +669,7 @@
 serviceNotPermitted={1} not permitted on ''{0}''
 sha1CollisionDetected=SHA-1 collision detected on {0}
 shallowCommitsAlreadyInitialized=Shallow commits have already been initialized
+shallowNotSupported=The server does not support shallow
 shallowPacksRequireDepthWalk=Shallow packs require a DepthWalk
 shortCompressedStreamAt=Short compressed stream at {0}
 shortReadOfBlock=Short read of block.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java
index 583767a..e7f40d8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java
@@ -9,7 +9,6 @@
  */
 package org.eclipse.jgit.api;
 
-import java.io.BufferedInputStream;
 import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.FileInputStream;
@@ -25,7 +24,6 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.zip.InflaterInputStream;
-
 import org.eclipse.jgit.api.errors.FilterFailedException;
 import org.eclipse.jgit.api.errors.GitAPIException;
 import org.eclipse.jgit.api.errors.PatchApplyException;
@@ -38,15 +36,11 @@
 import org.eclipse.jgit.dircache.DirCacheCheckout;
 import org.eclipse.jgit.dircache.DirCacheCheckout.CheckoutMetadata;
 import org.eclipse.jgit.dircache.DirCacheIterator;
-import org.eclipse.jgit.errors.LargeObjectException;
-import org.eclipse.jgit.errors.MissingObjectException;
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.CoreConfig.EolStreamType;
 import org.eclipse.jgit.lib.FileMode;
 import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectLoader;
-import org.eclipse.jgit.lib.ObjectStream;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.patch.BinaryHunk;
 import org.eclipse.jgit.patch.FileHeader;
@@ -162,13 +156,14 @@ public ApplyResult call() throws GitAPIException, PatchFormatException,
 								JGitText.get().renameFileFailed, f, dest), e);
 					}
 					apply(repository, fh.getOldPath(), cache, dest, fh);
+					r.addUpdatedFile(dest);
 					break;
 				case COPY:
-					f = getFile(fh.getOldPath(), false);
-					File target = getFile(fh.getNewPath(), false);
-					FileUtils.mkdirs(target.getParentFile(), true);
-					Files.copy(f.toPath(), target.toPath());
-					apply(repository, fh.getOldPath(), cache, target, fh);
+					File src = getFile(fh.getOldPath(), false);
+					f = getFile(fh.getNewPath(), false);
+					FileUtils.mkdirs(f.getParentFile(), true);
+					Files.copy(src.toPath(), f.toPath());
+					apply(repository, fh.getOldPath(), cache, f, fh);
 				}
 				r.addUpdatedFile(f);
 			}
@@ -355,60 +350,6 @@ private InputStream filterClean(Repository repository, String path,
 		return result.getStdout().openInputStreamWithAutoDestroy();
 	}
 
-	/**
-	 * Something that can supply an {@link InputStream}.
-	 */
-	private interface StreamSupplier {
-		InputStream load() throws IOException;
-	}
-
-	/**
-	 * We write the patch result to a {@link TemporaryBuffer} and then use
-	 * {@link DirCacheCheckout}.getContent() to run the result through the CR-LF
-	 * and smudge filters. DirCacheCheckout needs an ObjectLoader, not a
-	 * TemporaryBuffer, so this class bridges between the two, making any Stream
-	 * provided by a {@link StreamSupplier} look like an ordinary git blob to
-	 * DirCacheCheckout.
-	 */
-	private static class StreamLoader extends ObjectLoader {
-
-		private StreamSupplier data;
-
-		private long size;
-
-		StreamLoader(StreamSupplier data, long length) {
-			this.data = data;
-			this.size = length;
-		}
-
-		@Override
-		public int getType() {
-			return Constants.OBJ_BLOB;
-		}
-
-		@Override
-		public long getSize() {
-			return size;
-		}
-
-		@Override
-		public boolean isLarge() {
-			return true;
-		}
-
-		@Override
-		public byte[] getCachedBytes() throws LargeObjectException {
-			throw new LargeObjectException();
-		}
-
-		@Override
-		public ObjectStream openStream()
-				throws MissingObjectException, IOException {
-			return new ObjectStream.Filter(getType(), getSize(),
-					new BufferedInputStream(data.load()));
-		}
-	}
-
 	private void initHash(SHA1 hash, long size) {
 		hash.update(Constants.encodedTypeString(Constants.OBJ_BLOB));
 		hash.update((byte) ' ');
@@ -456,7 +397,7 @@ private void checkOid(ObjectId baseId, ObjectId id, ChangeType type, File f,
 	}
 
 	private void applyBinary(Repository repository, String path, File f,
-			FileHeader fh, StreamSupplier loader, ObjectId id,
+			FileHeader fh, DirCacheCheckout.StreamSupplier loader, ObjectId id,
 			CheckoutMetadata checkOut)
 			throws PatchApplyException, IOException {
 		if (!fh.getOldId().isComplete() || !fh.getNewId().isComplete()) {
@@ -488,8 +429,7 @@ private void applyBinary(Repository repository, String path, File f,
 														hunk.getBuffer(), start,
 														length))))) {
 					DirCacheCheckout.getContent(repository, path, checkOut,
-							new StreamLoader(() -> inflated, hunk.getSize()),
-							null, out);
+							() -> inflated, null, out);
 					if (!fh.getNewId().toObjectId().equals(hash.toObjectId())) {
 						throw new PatchApplyException(MessageFormat.format(
 								JGitText.get().applyBinaryResultOidWrong,
@@ -520,8 +460,7 @@ private void applyBinary(Repository repository, String path, File f,
 							SHA1InputStream hashed = new SHA1InputStream(hash,
 									input)) {
 						DirCacheCheckout.getContent(repository, path, checkOut,
-								new StreamLoader(() -> hashed, finalSize), null,
-								out);
+								() -> hashed, null, out);
 						if (!fh.getNewId().toObjectId()
 								.equals(hash.toObjectId())) {
 							throw new PatchApplyException(MessageFormat.format(
@@ -689,9 +628,7 @@ && canApplyAt(hunkLines, newLines, 0)) {
 			}
 			try (OutputStream output = new FileOutputStream(f)) {
 				DirCacheCheckout.getContent(repository, path, checkOut,
-						new StreamLoader(buffer::openInputStream,
-								buffer.length()),
-						null, output);
+						buffer::openInputStream, null, output);
 			}
 		} finally {
 			buffer.destroy();
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 847ab0a..7319ff4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java
@@ -55,6 +55,7 @@
 import org.eclipse.jgit.revwalk.RevTree;
 import org.eclipse.jgit.revwalk.RevWalk;
 import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.treewalk.WorkingTreeOptions;
 import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
 
 /**
@@ -411,6 +412,8 @@ public CheckoutCommand setAllPaths(boolean all) {
 	protected CheckoutCommand checkoutPaths() throws IOException,
 			RefNotFoundException {
 		actuallyModifiedPaths = new HashSet<>();
+		WorkingTreeOptions options = repo.getConfig()
+				.get(WorkingTreeOptions.KEY);
 		DirCache dc = repo.lockDirCache();
 		try (RevWalk revWalk = new RevWalk(repo);
 				TreeWalk treeWalk = new TreeWalk(repo,
@@ -419,10 +422,10 @@ protected CheckoutCommand checkoutPaths() throws IOException,
 			if (!checkoutAllPaths)
 				treeWalk.setFilter(PathFilterGroup.createFromStrings(paths));
 			if (isCheckoutIndex())
-				checkoutPathsFromIndex(treeWalk, dc);
+				checkoutPathsFromIndex(treeWalk, dc, options);
 			else {
 				RevCommit commit = revWalk.parseCommit(getStartPointObjectId());
-				checkoutPathsFromCommit(treeWalk, dc, commit);
+				checkoutPathsFromCommit(treeWalk, dc, commit, options);
 			}
 		} finally {
 			try {
@@ -439,7 +442,8 @@ protected CheckoutCommand checkoutPaths() throws IOException,
 		return this;
 	}
 
-	private void checkoutPathsFromIndex(TreeWalk treeWalk, DirCache dc)
+	private void checkoutPathsFromIndex(TreeWalk treeWalk, DirCache dc,
+			WorkingTreeOptions options)
 			throws IOException {
 		DirCacheIterator dci = new DirCacheIterator(dc);
 		treeWalk.addTree(dci);
@@ -465,8 +469,9 @@ public void apply(DirCacheEntry ent) {
 					if (stage > DirCacheEntry.STAGE_0) {
 						if (checkoutStage != null) {
 							if (stage == checkoutStage.number) {
-								checkoutPath(ent, r, new CheckoutMetadata(
-										eolStreamType, filterCommand));
+								checkoutPath(ent, r, options,
+										new CheckoutMetadata(eolStreamType,
+												filterCommand));
 								actuallyModifiedPaths.add(path);
 							}
 						} else {
@@ -475,8 +480,9 @@ public void apply(DirCacheEntry ent) {
 							throw new JGitInternalException(e.getMessage(), e);
 						}
 					} else {
-						checkoutPath(ent, r, new CheckoutMetadata(eolStreamType,
-								filterCommand));
+						checkoutPath(ent, r, options,
+								new CheckoutMetadata(eolStreamType,
+										filterCommand));
 						actuallyModifiedPaths.add(path);
 					}
 				}
@@ -488,7 +494,7 @@ public void apply(DirCacheEntry ent) {
 	}
 
 	private void checkoutPathsFromCommit(TreeWalk treeWalk, DirCache dc,
-			RevCommit commit) throws IOException {
+			RevCommit commit, WorkingTreeOptions options) throws IOException {
 		treeWalk.addTree(commit.getTree());
 		final ObjectReader r = treeWalk.getObjectReader();
 		DirCacheEditor editor = dc.editor();
@@ -510,7 +516,7 @@ public void apply(DirCacheEntry ent) {
 					}
 					ent.setObjectId(blobId);
 					ent.setFileMode(mode);
-					checkoutPath(ent, r,
+					checkoutPath(ent, r, options,
 							new CheckoutMetadata(eolStreamType, filterCommand));
 					actuallyModifiedPaths.add(path);
 				}
@@ -520,10 +526,10 @@ public void apply(DirCacheEntry ent) {
 	}
 
 	private void checkoutPath(DirCacheEntry entry, ObjectReader reader,
-			CheckoutMetadata checkoutMetadata) {
+			WorkingTreeOptions options, CheckoutMetadata checkoutMetadata) {
 		try {
 			DirCacheCheckout.checkoutEntry(repo, entry, reader, true,
-					checkoutMetadata);
+					checkoutMetadata, options);
 		} catch (IOException e) {
 			throw new JGitInternalException(MessageFormat.format(
 					JGitText.get().checkoutConflictWithFile,
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CleanCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CleanCommand.java
index 69272b7..36ca97d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CleanCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CleanCommand.java
@@ -25,6 +25,7 @@
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.util.FS;
 import org.eclipse.jgit.util.FileUtils;
+import org.eclipse.jgit.util.Paths;
 
 /**
  * Remove untracked files from the working tree
@@ -91,15 +92,16 @@ public Set<String> call() throws NoWorkTreeException, GitAPIException {
 			Set<String> notIgnoredDirs = filterIgnorePaths(untrackedDirs,
 					status.getIgnoredNotInIndex(), false);
 
-			for (String file : notIgnoredFiles)
+			for (String file : notIgnoredFiles) {
 				if (paths.isEmpty() || paths.contains(file)) {
 					files = cleanPath(file, files);
 				}
-
-			for (String dir : notIgnoredDirs)
+			}
+			for (String dir : notIgnoredDirs) {
 				if (paths.isEmpty() || paths.contains(dir)) {
 					files = cleanPath(dir, files);
 				}
+			}
 		} catch (IOException e) {
 			throw new JGitInternalException(e.getMessage(), e);
 		} finally {
@@ -142,14 +144,14 @@ private Set<String> cleanPath(String path, Set<String> inFiles)
 							FileUtils.delete(curFile, FileUtils.RECURSIVE
 									| FileUtils.SKIP_MISSING);
 						}
-						inFiles.add(path + "/"); //$NON-NLS-1$
+						inFiles.add(path + '/');
 					}
 				} else {
 					if (!dryRun) {
 						FileUtils.delete(curFile,
 								FileUtils.RECURSIVE | FileUtils.SKIP_MISSING);
 					}
-					inFiles.add(path + "/"); //$NON-NLS-1$
+					inFiles.add(path + '/');
 				}
 			}
 		} else {
@@ -166,14 +168,16 @@ private Set<String> filterIgnorePaths(Set<String> inputPaths,
 			Set<String> ignoredNotInIndex, boolean exact) {
 		if (ignore) {
 			Set<String> filtered = new TreeSet<>(inputPaths);
-			for (String path : inputPaths)
-				for (String ignored : ignoredNotInIndex)
+			for (String path : inputPaths) {
+				for (String ignored : ignoredNotInIndex) {
 					if ((exact && path.equals(ignored))
-							|| (!exact && path.startsWith(ignored))) {
+							|| (!exact
+									&& Paths.isEqualOrPrefix(ignored, path))) {
 						filtered.remove(path);
 						break;
 					}
-
+				}
+			}
 			return filtered;
 		}
 		return inputPaths;
@@ -182,14 +186,14 @@ private Set<String> filterIgnorePaths(Set<String> inputPaths,
 	private Set<String> filterFolders(Set<String> untracked,
 			Set<String> untrackedFolders) {
 		Set<String> filtered = new TreeSet<>(untracked);
-		for (String file : untracked)
-			for (String folder : untrackedFolders)
-				if (file.startsWith(folder)) {
+		for (String file : untracked) {
+			for (String folder : untrackedFolders) {
+				if (Paths.isEqualOrPrefix(folder, file)) {
 					filtered.remove(file);
 					break;
 				}
-
-
+			}
+		}
 		return filtered;
 	}
 
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 3aa7114..1f979a9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011, 2017 Chris Aniszczyk <caniszczyk@gmail.com> and others
+ * Copyright (C) 2011, 2022 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
@@ -13,10 +13,13 @@
 import java.io.IOException;
 import java.net.URISyntaxException;
 import java.text.MessageFormat;
+import java.time.Instant;
+import java.time.OffsetDateTime;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 
+import org.eclipse.jgit.annotations.NonNull;
 import org.eclipse.jgit.annotations.Nullable;
 import org.eclipse.jgit.api.errors.GitAPIException;
 import org.eclipse.jgit.api.errors.InvalidRemoteException;
@@ -91,6 +94,12 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> {
 
 	private TagOpt tagOption;
 
+	private Integer depth;
+
+	private Instant shallowSince;
+
+	private List<String> shallowExcludes = new ArrayList<>();
+
 	private enum FETCH_TYPE {
 		MULTIPLE_BRANCHES, ALL_BRANCHES, MIRROR
 	}
@@ -306,6 +315,11 @@ private FetchResult fetch(Repository clonedRepo, URIish u)
 					fetchAll ? TagOpt.FETCH_TAGS : TagOpt.AUTO_FOLLOW);
 		}
 		command.setInitialBranch(branch);
+		if (depth != null) {
+			command.setDepth(depth.intValue());
+		}
+		command.setShallowSince(shallowSince);
+		command.setShallowExcludes(shallowExcludes);
 		configure(command);
 
 		return command.call();
@@ -737,6 +751,82 @@ public CloneCommand setCallback(Callback callback) {
 		return this;
 	}
 
+	/**
+	 * Creates a shallow clone with a history truncated to the specified number
+	 * of commits.
+	 *
+	 * @param depth
+	 *            the depth
+	 * @return {@code this}
+	 *
+	 * @since 6.3
+	 */
+	public CloneCommand setDepth(int depth) {
+		if (depth < 1) {
+			throw new IllegalArgumentException(JGitText.get().depthMustBeAt1);
+		}
+		this.depth = Integer.valueOf(depth);
+		return this;
+	}
+
+	/**
+	 * Creates a shallow clone with a history after the specified time.
+	 *
+	 * @param shallowSince
+	 *            the timestammp; must not be {@code null}
+	 * @return {@code this}
+	 *
+	 * @since 6.3
+	 */
+	public CloneCommand setShallowSince(@NonNull OffsetDateTime shallowSince) {
+		this.shallowSince = shallowSince.toInstant();
+		return this;
+	}
+
+	/**
+	 * Creates a shallow clone with a history after the specified time.
+	 *
+	 * @param shallowSince
+	 *            the timestammp; must not be {@code null}
+	 * @return {@code this}
+	 *
+	 * @since 6.3
+	 */
+	public CloneCommand setShallowSince(@NonNull Instant shallowSince) {
+		this.shallowSince = shallowSince;
+		return this;
+	}
+
+	/**
+	 * Creates a shallow clone with a history, excluding commits reachable from
+	 * a specified remote branch or tag.
+	 *
+	 * @param shallowExclude
+	 *            the ref or commit; must not be {@code null}
+	 * @return {@code this}
+	 *
+	 * @since 6.3
+	 */
+	public CloneCommand addShallowExclude(@NonNull String shallowExclude) {
+		shallowExcludes.add(shallowExclude);
+		return this;
+	}
+
+	/**
+	 * Creates a shallow clone with a history, excluding commits reachable from
+	 * a specified remote branch or tag.
+	 *
+	 * @param shallowExclude
+	 *            the commit; must not be {@code null}
+	 * @return {@code this}
+	 *
+	 * @since 6.3
+	 */
+	public CloneCommand addShallowExclude(@NonNull ObjectId shallowExclude) {
+		shallowExcludes.add(shallowExclude.name());
+		return this;
+	}
+
 	private static void validateDirs(File directory, File gitDir, boolean bare)
 			throws IllegalStateException {
 		if (directory != null) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java
index 7290d83..38ece2f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010, Chris Aniszczyk <caniszczyk@gmail.com> and others
+ * Copyright (C) 2010, 2022 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
@@ -14,10 +14,13 @@
 import java.io.IOException;
 import java.net.URISyntaxException;
 import java.text.MessageFormat;
+import java.time.Instant;
+import java.time.OffsetDateTime;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
+import org.eclipse.jgit.annotations.NonNull;
 import org.eclipse.jgit.annotations.Nullable;
 import org.eclipse.jgit.api.errors.GitAPIException;
 import org.eclipse.jgit.api.errors.InvalidConfigurationException;
@@ -76,6 +79,14 @@ public class FetchCommand extends TransportCommand<FetchCommand, FetchResult> {
 
 	private String initialBranch;
 
+	private Integer depth;
+
+	private Instant deepenSince;
+
+	private List<String> shallowExcludes = new ArrayList<>();
+
+	private boolean unshallow;
+
 	/**
 	 * Callback for status of fetch operation.
 	 *
@@ -159,11 +170,9 @@ private void fetchSubmodules(FetchResult results)
 							walk.getPath());
 
 					// When the fetch mode is "yes" we always fetch. When the
-					// mode
-					// is "on demand", we only fetch if the submodule's revision
-					// was
-					// updated to an object that is not currently present in the
-					// submodule.
+					// mode is "on demand", we only fetch if the submodule's
+					// revision was updated to an object that is not currently
+					// present in the submodule.
 					if ((recurseMode == FetchRecurseSubmodulesMode.ON_DEMAND
 							&& !submoduleRepo.getObjectDatabase()
 									.has(walk.getObjectId()))
@@ -212,6 +221,17 @@ public FetchResult call() throws GitAPIException, InvalidRemoteException,
 			if (tagOption != null)
 				transport.setTagOpt(tagOption);
 			transport.setFetchThin(thin);
+			if (depth != null) {
+				transport.setDepth(depth);
+			}
+			if (unshallow) {
+				if (depth != null) {
+					throw new IllegalStateException(JGitText.get().depthWithUnshallow);
+				}
+				transport.setDepth(Constants.INFINITE_DEPTH);
+			}
+			transport.setDeepenSince(deepenSince);
+			transport.setDeepenNots(shallowExcludes);
 			configure(transport);
 			FetchResult result = transport.fetch(monitor,
 					applyOptions(refSpecs), initialBranch);
@@ -545,4 +565,105 @@ public FetchCommand setForceUpdate(boolean force) {
 		this.isForceUpdate = force;
 		return this;
 	}
+
+	/**
+	 * Limits fetching to the specified number of commits from the tip of each
+	 * remote branch history.
+	 *
+	 * @param depth
+	 *            the depth
+	 * @return {@code this}
+	 *
+	 * @since 6.3
+	 */
+	public FetchCommand setDepth(int depth) {
+		if (depth < 1) {
+			throw new IllegalArgumentException(JGitText.get().depthMustBeAt1);
+		}
+		this.depth = Integer.valueOf(depth);
+		return this;
+	}
+
+	/**
+	 * Deepens or shortens the history of a shallow repository to include all
+	 * reachable commits after a specified time.
+	 *
+	 * @param shallowSince
+	 *            the timestammp; must not be {@code null}
+	 * @return {@code this}
+	 *
+	 * @since 6.3
+	 */
+	public FetchCommand setShallowSince(@NonNull OffsetDateTime shallowSince) {
+		this.deepenSince = shallowSince.toInstant();
+		return this;
+	}
+
+	/**
+	 * Deepens or shortens the history of a shallow repository to include all
+	 * reachable commits after a specified time.
+	 *
+	 * @param shallowSince
+	 *            the timestammp; must not be {@code null}
+	 * @return {@code this}
+	 *
+	 * @since 6.3
+	 */
+	public FetchCommand setShallowSince(@NonNull Instant shallowSince) {
+		this.deepenSince = shallowSince;
+		return this;
+	}
+
+	/**
+	 * Deepens or shortens the history of a shallow repository to exclude
+	 * commits reachable from a specified remote branch or tag.
+	 *
+	 * @param shallowExclude
+	 *            the ref or commit; must not be {@code null}
+	 * @return {@code this}
+	 *
+	 * @since 6.3
+	 */
+	public FetchCommand addShallowExclude(@NonNull String shallowExclude) {
+		shallowExcludes.add(shallowExclude);
+		return this;
+	}
+
+	/**
+	 * Creates a shallow clone with a history, excluding commits reachable from
+	 * a specified remote branch or tag.
+	 *
+	 * @param shallowExclude
+	 *            the commit; must not be {@code null}
+	 * @return {@code this}
+	 *
+	 * @since 6.3
+	 */
+	public FetchCommand addShallowExclude(@NonNull ObjectId shallowExclude) {
+		shallowExcludes.add(shallowExclude.name());
+		return this;
+	}
+
+	/**
+	 * If the source repository is complete, converts a shallow repository to a
+	 * complete one, removing all the limitations imposed by shallow
+	 * repositories.
+	 *
+	 * If the source repository is shallow, fetches as much as possible so that
+	 * the current repository has the same history as the source repository.
+	 *
+	 * @param unshallow
+	 *            whether to unshallow or not
+	 * @return {@code this}
+	 *
+	 * @since 6.3
+	 */
+	public FetchCommand setUnshallow(boolean unshallow) {
+		this.unshallow = unshallow;
+		return this;
+	}
+
+	void setShallowExcludes(List<String> shallowExcludes) {
+		this.shallowExcludes = shallowExcludes;
+	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java
index 1004d3e..17036a9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java
@@ -48,6 +48,7 @@
 import org.eclipse.jgit.treewalk.AbstractTreeIterator;
 import org.eclipse.jgit.treewalk.FileTreeIterator;
 import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.treewalk.WorkingTreeOptions;
 
 /**
  * Command class to apply a stashed commit.
@@ -382,6 +383,8 @@ private void resetIndex(RevTree tree) throws IOException {
 	private void resetUntracked(RevTree tree) throws CheckoutConflictException,
 			IOException {
 		Set<String> actuallyModifiedPaths = new HashSet<>();
+		WorkingTreeOptions options = repo.getConfig()
+				.get(WorkingTreeOptions.KEY);
 		// TODO maybe NameConflictTreeWalk ?
 		try (TreeWalk walk = new TreeWalk(repo)) {
 			walk.addTree(tree);
@@ -413,7 +416,7 @@ private void resetUntracked(RevTree tree) throws CheckoutConflictException,
 					}
 				}
 
-				checkoutPath(entry, reader,
+				checkoutPath(entry, reader, options,
 						new CheckoutMetadata(eolStreamType, null));
 				actuallyModifiedPaths.add(entry.getPathString());
 			}
@@ -426,10 +429,10 @@ private void resetUntracked(RevTree tree) throws CheckoutConflictException,
 	}
 
 	private void checkoutPath(DirCacheEntry entry, ObjectReader reader,
-			CheckoutMetadata checkoutMetadata) {
+			WorkingTreeOptions options, CheckoutMetadata checkoutMetadata) {
 		try {
 			DirCacheCheckout.checkoutEntry(repo, entry, reader, true,
-					checkoutMetadata);
+					checkoutMetadata, options);
 		} catch (IOException e) {
 			throw new JGitInternalException(MessageFormat.format(
 					JGitText.get().checkoutConflictWithFile,
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameGenerator.java
index 10d7752..77967df 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameGenerator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameGenerator.java
@@ -41,6 +41,7 @@
 import org.eclipse.jgit.dircache.DirCacheIterator;
 import org.eclipse.jgit.errors.NoWorkTreeException;
 import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.diff.FilteredRenameDetector;
 import org.eclipse.jgit.lib.AnyObjectId;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.MutableObjectId;
@@ -1109,9 +1110,10 @@ private DiffEntry findRename(RevCommit parent, RevCommit commit,
 
 		treeWalk.setFilter(TreeFilter.ANY_DIFF);
 		treeWalk.reset(parent.getTree(), commit.getTree());
-		renameDetector.reset();
-		renameDetector.addAll(DiffEntry.scan(treeWalk));
-		for (DiffEntry ent : renameDetector.compute()) {
+		List<DiffEntry> diffs = DiffEntry.scan(treeWalk);
+		FilteredRenameDetector filteredRenameDetector = new FilteredRenameDetector(
+				renameDetector);
+		for (DiffEntry ent : filteredRenameDetector.compute(diffs, path)) {
 			if (isRename(ent) && ent.getNewPath().equals(path.getPath()))
 				return ent;
 		}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
index f6fc393..1fb81b7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
@@ -21,6 +21,7 @@
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.OutputStream;
 import java.nio.file.StandardCopyOption;
 import java.text.MessageFormat;
@@ -143,6 +144,8 @@ public CheckoutMetadata(EolStreamType eolStreamType,
 
 	private boolean performingCheckout;
 
+	private WorkingTreeOptions options;
+
 	private ProgressMonitor monitor = NullProgressMonitor.INSTANCE;
 
 	/**
@@ -362,9 +365,12 @@ private int addTree(TreeWalk tw, ObjectId id) throws MissingObjectException,
 	 * Processing an entry in the context of {@link #prescanOneTree()} when only
 	 * one tree is given
 	 *
-	 * @param m the tree to merge
-	 * @param i the index
-	 * @param f the working tree
+	 * @param m
+	 *            the tree to merge
+	 * @param i
+	 *            the index
+	 * @param f
+	 *            the working tree
 	 * @throws IOException
 	 */
 	void processEntry(CanonicalTreeParser m, DirCacheBuildIterator i,
@@ -489,6 +495,8 @@ private boolean doCheckout() throws CorruptObjectException, IOException,
 			MissingObjectException, IncorrectObjectTypeException,
 			CheckoutConflictException, IndexWriteException, CanceledException {
 		toBeDeleted.clear();
+		options = repo.getConfig()
+				.get(WorkingTreeOptions.KEY);
 		try (ObjectReader objectReader = repo.getObjectDatabase().newReader()) {
 			if (headCommitTree != null)
 				preScanTwoTrees();
@@ -558,7 +566,8 @@ private boolean doCheckout() throws CorruptObjectException, IOException,
 					if (FileMode.GITLINK.equals(entry.getRawMode())) {
 						checkoutGitlink(path, entry);
 					} else {
-						checkoutEntry(repo, entry, objectReader, false, meta);
+						checkoutEntry(repo, entry, objectReader, false, meta,
+								options);
 					}
 					e = null;
 
@@ -594,7 +603,7 @@ private boolean doCheckout() throws CorruptObjectException, IOException,
 						}
 						if (entry.getStage() == DirCacheEntry.STAGE_3) {
 							checkoutEntry(repo, entry, objectReader, false,
-									null);
+									null, options);
 							break;
 						}
 						++entryIdx;
@@ -1226,7 +1235,7 @@ private void keep(String path, DirCacheEntry e, WorkingTreeIterator f)
 				checkoutEntry(repo, e, walk.getObjectReader(), false,
 						new CheckoutMetadata(walk.getEolStreamType(CHECKOUT_OP),
 								walk.getFilterCommand(
-										Constants.ATTR_FILTER_TYPE_SMUDGE)));
+										Constants.ATTR_FILTER_TYPE_SMUDGE)), options);
 			}
 		}
 	}
@@ -1392,12 +1401,6 @@ private boolean isModifiedSubtree_IndexTree(String path, ObjectId tree)
 	 * cannot be renamed to file or link without deleting it recursively.
 	 * </p>
 	 *
-	 * <p>
-	 * TODO: this method works directly on File IO, we may need another
-	 * abstraction (like WorkingTreeIterator). This way we could tell e.g.
-	 * Eclipse that Files in the workspace got changed
-	 * </p>
-	 *
 	 * @param repo
 	 *            repository managing the destination work tree.
 	 * @param entry
@@ -1407,15 +1410,16 @@ private boolean isModifiedSubtree_IndexTree(String path, ObjectId tree)
 	 * @throws java.io.IOException
 	 * @since 3.6
 	 * @deprecated since 5.1, use
-	 *             {@link #checkoutEntry(Repository, DirCacheEntry, ObjectReader, boolean, CheckoutMetadata)}
+	 *             {@link #checkoutEntry(Repository, DirCacheEntry, ObjectReader, boolean, CheckoutMetadata, WorkingTreeOptions)}
 	 *             instead
 	 */
 	@Deprecated
 	public static void checkoutEntry(Repository repo, DirCacheEntry entry,
 			ObjectReader or) throws IOException {
-		checkoutEntry(repo, entry, or, false, null);
+		checkoutEntry(repo, entry, or, false, null, null);
 	}
 
+
 	/**
 	 * Updates the file in the working tree with content and mode from an entry
 	 * in the index. The new content is first written to a new temporary file in
@@ -1428,12 +1432,6 @@ public static void checkoutEntry(Repository repo, DirCacheEntry entry,
 	 * recursively, independently if has any content.
 	 * </p>
 	 *
-	 * <p>
-	 * TODO: this method works directly on File IO, we may need another
-	 * abstraction (like WorkingTreeIterator). This way we could tell e.g.
-	 * Eclipse that Files in the workspace got changed
-	 * </p>
-	 *
 	 * @param repo
 	 *            repository managing the destination work tree.
 	 * @param entry
@@ -1452,12 +1450,58 @@ public static void checkoutEntry(Repository repo, DirCacheEntry entry,
 	 *            </ul>
 	 * @throws java.io.IOException
 	 * @since 4.2
+	 * @deprecated since 6.3, use
+	 *             {@link #checkoutEntry(Repository, DirCacheEntry, ObjectReader, boolean, CheckoutMetadata, WorkingTreeOptions)}
+	 *             instead
 	 */
+	@Deprecated
 	public static void checkoutEntry(Repository repo, DirCacheEntry entry,
 			ObjectReader or, boolean deleteRecursive,
 			CheckoutMetadata checkoutMetadata) throws IOException {
-		if (checkoutMetadata == null)
+		checkoutEntry(repo, entry, or, deleteRecursive, checkoutMetadata, null);
+	}
+
+	/**
+	 * Updates the file in the working tree with content and mode from an entry
+	 * in the index. The new content is first written to a new temporary file in
+	 * the same directory as the real file. Then that new file is renamed to the
+	 * final filename.
+	 *
+	 * <p>
+	 * <b>Note:</b> if the entry path on local file system exists as a file, it
+	 * will be deleted and if it exists as a directory, it will be deleted
+	 * recursively, independently if has any content.
+	 * </p>
+	 *
+	 * @param repo
+	 *            repository managing the destination work tree.
+	 * @param entry
+	 *            the entry containing new mode and content
+	 * @param or
+	 *            object reader to use for checkout
+	 * @param deleteRecursive
+	 *            true to recursively delete final path if it exists on the file
+	 *            system
+	 * @param checkoutMetadata
+	 *            containing
+	 *            <ul>
+	 *            <li>smudgeFilterCommand to be run for smudging the entry to be
+	 *            checked out</li>
+	 *            <li>eolStreamType used for stream conversion</li>
+	 *            </ul>
+	 * @param options
+	 *            {@link WorkingTreeOptions} that are effective; if {@code null}
+	 *            they are loaded from the repository config
+	 * @throws java.io.IOException
+	 * @since 6.3
+	 */
+	public static void checkoutEntry(Repository repo, DirCacheEntry entry,
+			ObjectReader or, boolean deleteRecursive,
+			CheckoutMetadata checkoutMetadata, WorkingTreeOptions options)
+			throws IOException {
+		if (checkoutMetadata == null) {
 			checkoutMetadata = CheckoutMetadata.EMPTY;
+		}
 		ObjectLoader ol = or.open(entry.getObjectId());
 		File f = new File(repo.getWorkTree(), entry.getPathString());
 		File parentDir = f.getParentFile();
@@ -1466,7 +1510,8 @@ public static void checkoutEntry(Repository repo, DirCacheEntry entry,
 		}
 		FileUtils.mkdirs(parentDir, true);
 		FS fs = repo.getFS();
-		WorkingTreeOptions opt = repo.getConfig().get(WorkingTreeOptions.KEY);
+		WorkingTreeOptions opt = options != null ? options
+				: repo.getConfig().get(WorkingTreeOptions.KEY);
 		if (entry.getFileMode() == FileMode.SYMLINK
 				&& opt.getSymLinks() == SymLinks.TRUE) {
 			byte[] bytes = ol.getBytes();
@@ -1561,6 +1606,60 @@ public static void getContent(Repository repo, String path,
 			CheckoutMetadata checkoutMetadata, ObjectLoader ol,
 			WorkingTreeOptions opt, OutputStream os)
 			throws IOException {
+		getContent(repo, path, checkoutMetadata, ol::openStream, opt, os);
+	}
+
+
+	/**
+	 * Something that can supply an {@link InputStream}.
+	 *
+	 * @since 6.3
+	 */
+	public interface StreamSupplier {
+
+		/**
+		 * Loads the input stream.
+		 *
+		 * @return the loaded stream
+		 * @throws IOException
+		 *             if any reading error occurs
+		 */
+		InputStream load() throws IOException;
+	}
+
+	/**
+	 * Return filtered content for blob contents. EOL handling and smudge-filter
+	 * handling are applied in the same way as it would be done during a
+	 * checkout.
+	 *
+	 * @param repo
+	 *            the repository
+	 * @param path
+	 *            the path used to determine the correct filters for the object
+	 * @param checkoutMetadata
+	 *            containing
+	 *            <ul>
+	 *            <li>smudgeFilterCommand to be run for smudging the object</li>
+	 *            <li>eolStreamType used for stream conversion (can be
+	 *            null)</li>
+	 *            </ul>
+	 * @param inputStream
+	 *            A supplier for the raw content of the object. Each call should
+	 *            yield a fresh stream of the same object.
+	 * @param opt
+	 *            the working tree options where only 'core.autocrlf' is used
+	 *            for EOL handling if 'checkoutMetadata.eolStreamType' is not
+	 *            valid
+	 * @param os
+	 *            the output stream the filtered content is written to. The
+	 *            caller is responsible to close the stream.
+	 * @throws IOException
+	 * @since 6.3
+	 */
+	public static void getContent(Repository repo, String path,
+			CheckoutMetadata checkoutMetadata, StreamSupplier inputStream,
+			WorkingTreeOptions opt, OutputStream os)
+			throws IOException {
 		EolStreamType nonNullEolStreamType;
 		if (checkoutMetadata.eolStreamType != null) {
 			nonNullEolStreamType = checkoutMetadata.eolStreamType;
@@ -1574,21 +1673,23 @@ public static void getContent(Repository repo, String path,
 			if (checkoutMetadata.smudgeFilterCommand != null) {
 				if (FilterCommandRegistry
 						.isRegistered(checkoutMetadata.smudgeFilterCommand)) {
-					runBuiltinFilterCommand(repo, checkoutMetadata, ol,
+					runBuiltinFilterCommand(repo, checkoutMetadata, inputStream,
 							channel);
 				} else {
-					runExternalFilterCommand(repo, path, checkoutMetadata, ol,
+					runExternalFilterCommand(repo, path, checkoutMetadata, inputStream,
 							channel);
 				}
 			} else {
-				ol.copyTo(channel);
+				try (InputStream in = inputStream.load()) {
+					in.transferTo(channel);
+				}
 			}
 		}
 	}
 
 	// Run an external filter command
 	private static void runExternalFilterCommand(Repository repo, String path,
-			CheckoutMetadata checkoutMetadata, ObjectLoader ol,
+			CheckoutMetadata checkoutMetadata, StreamSupplier inputStream,
 			OutputStream channel) throws IOException {
 		FS fs = repo.getFS();
 		ProcessBuilder filterProcessBuilder = fs.runInShell(
@@ -1600,7 +1701,9 @@ private static void runExternalFilterCommand(Repository repo, String path,
 		int rc;
 		try {
 			// TODO: wire correctly with AUTOCRLF
-			result = fs.execute(filterProcessBuilder, ol.openStream());
+			try (InputStream in = inputStream.load()) {
+				result = fs.execute(filterProcessBuilder, in);
+			}
 			rc = result.getRc();
 			if (rc == 0) {
 				result.getStdout().writeTo(channel,
@@ -1621,31 +1724,35 @@ private static void runExternalFilterCommand(Repository repo, String path,
 
 	// Run a builtin filter command
 	private static void runBuiltinFilterCommand(Repository repo,
-			CheckoutMetadata checkoutMetadata, ObjectLoader ol,
+			CheckoutMetadata checkoutMetadata, StreamSupplier inputStream,
 			OutputStream channel) throws MissingObjectException, IOException {
 		boolean isMandatory = repo.getConfig().getBoolean(
 				ConfigConstants.CONFIG_FILTER_SECTION,
 				ConfigConstants.CONFIG_SECTION_LFS,
 				ConfigConstants.CONFIG_KEY_REQUIRED, false);
 		FilterCommand command = null;
-		try {
-			command = FilterCommandRegistry.createFilterCommand(
-					checkoutMetadata.smudgeFilterCommand, repo, ol.openStream(),
-					channel);
-		} catch (IOException e) {
-			LOG.error(JGitText.get().failedToDetermineFilterDefinition, e);
-			if (!isMandatory) {
-				// In case an IOException occurred during creating of the
-				// command then proceed as if there would not have been a
-				// builtin filter (only if the filter is not mandatory).
-				ol.copyTo(channel);
-			} else {
-				throw e;
+		try (InputStream in = inputStream.load()) {
+			try {
+				command = FilterCommandRegistry.createFilterCommand(
+						checkoutMetadata.smudgeFilterCommand, repo, in,
+						channel);
+			} catch (IOException e) {
+				LOG.error(JGitText.get().failedToDetermineFilterDefinition, e);
+				if (!isMandatory) {
+					// In case an IOException occurred during creating of the
+					// command then proceed as if there would not have been a
+					// builtin filter (only if the filter is not mandatory).
+					try (InputStream again = inputStream.load()) {
+						again.transferTo(channel);
+					}
+				} else {
+					throw e;
+				}
 			}
-		}
-		if (command != null) {
-			while (command.run() != -1) {
-				// loop as long as command.run() tells there is work to do
+			if (command != null) {
+				while (command.run() != -1) {
+					// loop as long as command.run() tells there is work to do
+				}
 			}
 		}
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/BareSuperprojectWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/BareSuperprojectWriter.java
index e6626ae..f63cc6d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/BareSuperprojectWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/BareSuperprojectWriter.java
@@ -23,6 +23,7 @@
 import org.eclipse.jgit.dircache.DirCache;
 import org.eclipse.jgit.dircache.DirCacheBuilder;
 import org.eclipse.jgit.dircache.DirCacheEntry;
+import org.eclipse.jgit.dircache.InvalidPathException;
 import org.eclipse.jgit.gitrepo.RepoCommand.ManifestErrorException;
 import org.eclipse.jgit.gitrepo.RepoCommand.RemoteFile;
 import org.eclipse.jgit.gitrepo.RepoCommand.RemoteReader;
@@ -138,7 +139,7 @@ RevCommit write(List<RepoProject> repoProjects)
 			}
 			// In the last try, just propagate the exceptions
 			return commitTreeOnCurrentTip(inserter, rw, treeId);
-		} catch (IOException | InterruptedException e) {
+		} catch (IOException | InterruptedException | InvalidPathException e) {
 			throw new ManifestErrorException(e);
 		}
 	}
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 d4e3cca..b4e2ff4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
@@ -266,6 +266,8 @@ public static JGitText get() {
 	/***/ public String deleteRequiresZeroNewId;
 	/***/ public String deleteTagUnexpectedResult;
 	/***/ public String deletingNotSupported;
+	/***/ public String depthMustBeAt1;
+	/***/ public String depthWithUnshallow;
 	/***/ public String destinationIsNotAWildcard;
 	/***/ public String detachedHeadDetected;
 	/***/ public String diffToolNotGivenError;
@@ -549,6 +551,7 @@ public static JGitText get() {
 	/***/ public String nothingToFetch;
 	/***/ public String nothingToPush;
 	/***/ public String notMergedExceptionMessage;
+	/***/ public String notShallowedUnshallow;
 	/***/ public String noXMLParserAvailable;
 	/***/ public String objectAtHasBadZlibStream;
 	/***/ public String objectIsCorrupt;
@@ -618,6 +621,7 @@ public static JGitText get() {
 	/***/ public String pushOptionsNotSupported;
 	/***/ public String rawLogMessageDoesNotParseAsLogEntry;
 	/***/ public String readConfigFailed;
+	/***/ public String readShallowFailed;
 	/***/ public String readFileStoreAttributesFailed;
 	/***/ public String readerIsRequired;
 	/***/ public String readingObjectsFromLocalRepositoryFailed;
@@ -693,6 +697,7 @@ public static JGitText get() {
 	/***/ public String serviceNotPermitted;
 	/***/ public String sha1CollisionDetected;
 	/***/ public String shallowCommitsAlreadyInitialized;
+	/***/ public String shallowNotSupported;
 	/***/ public String shallowPacksRequireDepthWalk;
 	/***/ public String shortCompressedStreamAt;
 	/***/ public String shortReadOfBlock;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diff/FilteredRenameDetector.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diff/FilteredRenameDetector.java
new file mode 100644
index 0000000..d65624f
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diff/FilteredRenameDetector.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2022, Simeon Andreev 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.diff;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.jgit.diff.DiffEntry;
+import org.eclipse.jgit.diff.DiffEntry.ChangeType;
+import org.eclipse.jgit.diff.RenameDetector;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.treewalk.filter.PathFilter;
+
+/**
+ * Provides rename detection in special cases such as blame, where only a subset
+ * of the renames detected by {@link RenameDetector} is of interest.
+ */
+public class FilteredRenameDetector {
+
+	private final RenameDetector renameDetector;
+
+	/**
+	 * @param repository
+	 *            The repository in which to check for renames.
+	 */
+	public FilteredRenameDetector(Repository repository) {
+		this(new RenameDetector(repository));
+	}
+
+	/**
+	 * @param renameDetector
+	 *            The {@link RenameDetector} to use when checking for renames.
+	 */
+	public FilteredRenameDetector(RenameDetector renameDetector) {
+		this.renameDetector = renameDetector;
+	}
+
+	/**
+	 * @param diffs
+	 *            The set of changes to check.
+	 * @param pathFilter
+	 *            Filter out changes that didn't affect this path.
+	 * @return The subset of changes that affect only the filtered path.
+	 * @throws IOException
+	 */
+	public List<DiffEntry> compute(List<DiffEntry> diffs,
+			PathFilter pathFilter) throws IOException {
+		return compute(diffs, Arrays.asList(pathFilter));
+	}
+
+	/**
+	 * Tries to avoid computation overhead in {@link RenameDetector#compute()}
+	 * by filtering diffs related to the path filters only.
+	 * <p>
+	 * Note: current implementation only optimizes added or removed diffs,
+	 * further optimization is possible.
+	 *
+	 * @param changes
+	 *            The set of changes to check.
+	 * @param pathFilters
+	 *            Filter out changes that didn't affect these paths.
+	 * @return The subset of changes that affect only the filtered paths.
+	 * @throws IOException
+	 * @see RenameDetector#compute()
+	 */
+	public List<DiffEntry> compute(List<DiffEntry> changes,
+			List<PathFilter> pathFilters) throws IOException {
+
+		if (pathFilters == null) {
+			throw new IllegalArgumentException("Must specify path filters"); //$NON-NLS-1$
+		}
+
+		Set<String> paths = new HashSet<>(pathFilters.size());
+		for (PathFilter pathFilter : pathFilters) {
+			paths.add(pathFilter.getPath());
+		}
+
+		List<DiffEntry> filtered = new ArrayList<>();
+
+		// For new path: skip ADD's that don't match given paths
+		for (DiffEntry diff : changes) {
+			ChangeType changeType = diff.getChangeType();
+			if (changeType != ChangeType.ADD
+					|| paths.contains(diff.getNewPath())) {
+				filtered.add(diff);
+			}
+		}
+
+		renameDetector.reset();
+		renameDetector.addAll(filtered);
+		List<DiffEntry> sourceChanges = renameDetector.compute();
+
+		filtered.clear();
+
+		// For old path: skip DELETE's that don't match given paths
+		for (DiffEntry diff : changes) {
+			ChangeType changeType = diff.getChangeType();
+			if (changeType != ChangeType.DELETE
+					|| paths.contains(diff.getOldPath())) {
+				filtered.add(diff);
+			}
+		}
+
+		renameDetector.reset();
+		renameDetector.addAll(filtered);
+		List<DiffEntry> targetChanges = renameDetector.compute();
+
+		List<DiffEntry> result = new ArrayList<>();
+
+		for (DiffEntry sourceChange : sourceChanges) {
+			if (paths.contains(sourceChange.getNewPath())) {
+				result.add(sourceChange);
+			}
+		}
+		for (DiffEntry targetChange : targetChanges) {
+			if (paths.contains(targetChange.getOldPath())) {
+				result.add(targetChange);
+			}
+		}
+
+		renameDetector.reset();
+		return result;
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/CommandExecutor.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/CommandExecutor.java
index 668adea..ebef524 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/CommandExecutor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/CommandExecutor.java
@@ -27,6 +27,7 @@
 import org.eclipse.jgit.util.FS_Win32;
 import org.eclipse.jgit.util.FS_Win32_Cygwin;
 import org.eclipse.jgit.util.StringUtils;
+import org.eclipse.jgit.util.SystemReader;
 
 /**
  * Runs a command with help of FS.
@@ -87,7 +88,9 @@ public ExecutionResult run(String command, File workingDir,
 									+ "execError: " + execError + "\n" //$NON-NLS-1$ //$NON-NLS-2$
 									+ "stderr: \n" //$NON-NLS-1$
 									+ new String(
-											result.getStderr().toByteArray()),
+											result.getStderr().toByteArray(),
+											SystemReader.getInstance()
+													.getDefaultCharset()),
 							result, execError);
 				}
 			}
@@ -202,7 +205,8 @@ private void createCommandFile(String command)
 		commandFile = File.createTempFile(".__", //$NON-NLS-1$
 				"__jgit_tool" + fileExtension); //$NON-NLS-1$
 		try (OutputStream outStream = new FileOutputStream(commandFile)) {
-			byte[] strToBytes = command.getBytes();
+			byte[] strToBytes = command
+					.getBytes(SystemReader.getInstance().getDefaultCharset());
 			outStream.write(strToBytes);
 			outStream.close();
 		}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/DiffTools.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/DiffTools.java
index 7cedd82..d0034df 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/DiffTools.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/DiffTools.java
@@ -103,7 +103,7 @@ private DiffTools(Repository repo, StoredConfig config) {
 	 * @param noToolHandler
 	 *            The handler to use when needing to inform the user, that no
 	 *            tool is configured.
-	 * @return the optioanl result of executing the tool if it was executed
+	 * @return the optional result of executing the tool if it was executed
 	 * @throws ToolException
 	 *             when the tool fails
 	 */
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/ToolException.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/ToolException.java
index 7cc5bb5..73d3588 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/ToolException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/ToolException.java
@@ -11,7 +11,7 @@
 package org.eclipse.jgit.internal.diffmergetool;
 
 import org.eclipse.jgit.util.FS.ExecutionResult;
-
+import org.eclipse.jgit.util.SystemReader;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -114,7 +114,8 @@ public String getResultStderr() {
 			return ""; //$NON-NLS-1$
 		}
 		try {
-			return new String(result.getStderr().toByteArray());
+			return new String(result.getStderr().toByteArray(),
+					SystemReader.getInstance().getDefaultCharset());
 		} catch (Exception e) {
 			LOG.warn("Failed to retrieve standard error output", e); //$NON-NLS-1$
 		}
@@ -129,7 +130,8 @@ public String getResultStdout() {
 			return ""; //$NON-NLS-1$
 		}
 		try {
-			return new String(result.getStdout().toByteArray());
+			return new String(result.getStdout().toByteArray(),
+					SystemReader.getInstance().getDefaultCharset());
 		} catch (Exception e) {
 			LOG.warn("Failed to retrieve standard output", e); //$NON-NLS-1$
 		}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/InMemoryRepository.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/InMemoryRepository.java
index 99da222..5a8207e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/InMemoryRepository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/InMemoryRepository.java
@@ -1,3 +1,12 @@
+/*
+ * Copyright (C) 2011, 2022 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.dfs;
 
 import java.io.ByteArrayOutputStream;
@@ -6,13 +15,16 @@
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import org.eclipse.jgit.annotations.Nullable;
 import org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource;
 import org.eclipse.jgit.internal.storage.pack.PackExt;
 import org.eclipse.jgit.internal.storage.reftable.ReftableConfig;
+import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.RefDatabase;
 
 /**
@@ -98,6 +110,7 @@ public void setGitwebDescription(@Nullable String d) {
 	public static class MemObjDatabase extends DfsObjDatabase {
 		private List<DfsPackDescription> packs = new ArrayList<>();
 		private int blockSize;
+		private Set<ObjectId> shallowCommits = Collections.emptySet();
 
 		MemObjDatabase(DfsRepository repo) {
 			super(repo, new DfsReaderOptions());
@@ -167,6 +180,16 @@ public void flush() {
 		}
 
 		@Override
+		public Set<ObjectId> getShallowCommits() throws IOException {
+			return shallowCommits;
+		}
+
+		@Override
+		public void setShallowCommits(Set<ObjectId> shallowCommits) {
+			this.shallowCommits = shallowCommits;
+		}
+
+		@Override
 		public long getApproximateObjectCount() {
 			long count = 0;
 			for (DfsPackDescription p : packs) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/CachedObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/CachedObjectDirectory.java
index 094fdc1..9272bf3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/CachedObjectDirectory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/CachedObjectDirectory.java
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2010, Constantine Plotnikov <constantine.plotnikov@gmail.com>
- * Copyright (C) 2010, JetBrains s.r.o. and others
+ * Copyright (C) 2010, 2022 JetBrains s.r.o. 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,10 +117,15 @@ FS getFS() {
 	}
 
 	@Override
-	Set<ObjectId> getShallowCommits() throws IOException {
+	public Set<ObjectId> getShallowCommits() throws IOException {
 		return wrapped.getShallowCommits();
 	}
 
+    @Override
+    public void setShallowCommits(Set<ObjectId> shallowCommits) throws IOException {
+        wrapped.setShallowCommits(shallowCommits);
+    }
+
 	private CachedObjectDirectory[] myAlternates() {
 		if (alts == null) {
 			ObjectDirectory.AlternateHandle[] src = wrapped.myAlternates();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileObjectDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileObjectDatabase.java
index 01dd27d..e97ed39 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileObjectDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileObjectDatabase.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010, Google Inc. and others
+ * Copyright (C) 2010, 2022 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
@@ -50,8 +50,6 @@ abstract void resolve(Set<ObjectId> matches, AbbreviatedObjectId id)
 
 	abstract FS getFS();
 
-	abstract Set<ObjectId> getShallowCommits() throws IOException;
-
 	abstract void selectObjectRepresentation(PackWriter packer,
 			ObjectToPack otp, WindowCursor curs) throws IOException;
 
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 531fd78..64be4ae 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
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009, Google Inc. and others
+ * Copyright (C) 2009, 2022 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
@@ -19,6 +19,7 @@
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.io.OutputStream;
 import java.nio.file.Files;
 import java.text.MessageFormat;
 import java.util.ArrayList;
@@ -560,33 +561,84 @@ FS getFS() {
 	}
 
 	@Override
-	Set<ObjectId> getShallowCommits() throws IOException {
+	public Set<ObjectId> getShallowCommits() throws IOException {
 		if (shallowFile == null || !shallowFile.isFile())
 			return Collections.emptySet();
 
 		if (shallowFileSnapshot == null
 				|| shallowFileSnapshot.isModified(shallowFile)) {
-			shallowCommitsIds = new HashSet<>();
+			try {
+				shallowCommitsIds = FileUtils.readWithRetries(shallowFile,
+						f -> {
+							FileSnapshot newSnapshot = FileSnapshot.save(f);
+							HashSet<ObjectId> result = new HashSet<>();
+							try (BufferedReader reader = open(f)) {
+								String line;
+								while ((line = reader.readLine()) != null) {
+									if (!ObjectId.isId(line)) {
+										throw new IOException(
+												MessageFormat.format(JGitText
+														.get().badShallowLine,
+														f.getAbsolutePath(),
+														line));
 
-			try (BufferedReader reader = open(shallowFile)) {
-				String line;
-				while ((line = reader.readLine()) != null) {
-					try {
-						shallowCommitsIds.add(ObjectId.fromString(line));
-					} catch (IllegalArgumentException ex) {
-						throw new IOException(MessageFormat
-								.format(JGitText.get().badShallowLine, line),
-								ex);
-					}
-				}
+									}
+									result.add(ObjectId.fromString(line));
+								}
+							}
+							shallowFileSnapshot = newSnapshot;
+							return result;
+						});
+			} catch (IOException e) {
+				throw e;
+			} catch (Exception e) {
+				throw new IOException(
+						MessageFormat.format(JGitText.get().readShallowFailed,
+								shallowFile.getAbsolutePath()),
+						e);
 			}
-
-			shallowFileSnapshot = FileSnapshot.save(shallowFile);
 		}
 
 		return shallowCommitsIds;
 	}
 
+	@Override
+	public void setShallowCommits(Set<ObjectId> shallowCommits) throws IOException {
+		this.shallowCommitsIds = shallowCommits;
+		LockFile lock = new LockFile(shallowFile);
+		if (!lock.lock()) {
+			throw new IOException(MessageFormat.format(JGitText.get().lockError,
+					shallowFile.getAbsolutePath()));
+		}
+
+		try {
+			if (shallowCommits.isEmpty()) {
+				if (shallowFile.isFile()) {
+					shallowFile.delete();
+				}
+			} else {
+				try (OutputStream out = lock.getOutputStream()) {
+					for (ObjectId shallowCommit : shallowCommits) {
+						byte[] buf = new byte[Constants.OBJECT_ID_STRING_LENGTH + 1];
+						shallowCommit.copyTo(buf, 0);
+						buf[Constants.OBJECT_ID_STRING_LENGTH] = '\n';
+						out.write(buf);
+					}
+				} finally {
+					lock.commit();
+				}
+			}
+		} finally {
+			lock.unlock();
+		}
+
+		if (shallowCommits.isEmpty()) {
+			shallowFileSnapshot = FileSnapshot.DIRTY;
+		} else {
+			shallowFileSnapshot = FileSnapshot.save(shallowFile);
+		}
+	}
+
 	void closeAllPackHandles(File packFile) {
 		// if the packfile already exists (because we are rewriting a
 		// packfile for the same set of objects maybe with different
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
index cf2e69d..30a0074 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
@@ -1,7 +1,7 @@
 /*
  * Copyright (C) 2008, Google Inc.
  * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2006-2017, Shawn O. Pearce <spearce@spearce.org> and others
+ * Copyright (C) 2006, 2022, 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
@@ -747,6 +747,13 @@ public static int decodeTypeString(final AnyObjectId id,
 	 */
 	public static final String LOCK_SUFFIX = ".lock"; //$NON-NLS-1$
 
+	/**
+	 * Depth used to unshallow a repository
+	 *
+	 * @since 6.3
+	 */
+	public static final int INFINITE_DEPTH = 0x7fffffff;
+
 	private Constants() {
 		// Hide the default constructor
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDatabase.java
index 70009cb..a39766c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDatabase.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009, Google Inc. and others
+ * Copyright (C) 2009, 2022 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,8 @@
 package org.eclipse.jgit.lib;
 
 import java.io.IOException;
+import java.util.Collections;
+import java.util.Set;
 
 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
 import org.eclipse.jgit.errors.MissingObjectException;
@@ -22,6 +24,9 @@
  * {@link org.eclipse.jgit.lib.ObjectId}.
  */
 public abstract class ObjectDatabase implements AutoCloseable {
+
+	private static final Set<ObjectId> shallowCommits = Collections.emptySet();
+
 	/**
 	 * Initialize a new database instance for access.
 	 */
@@ -72,6 +77,35 @@ public void create() throws IOException {
 	public abstract ObjectReader newReader();
 
 	/**
+	 * @return the shallow commits of the current repository
+	 *
+	 * @throws IOException the database could not be read
+	 *
+	 * @since 6.3
+	 */
+	public Set<ObjectId> getShallowCommits() throws IOException {
+		return shallowCommits;
+	}
+
+
+	/**
+	 * Update the shallow commits of the current repository
+	 *
+	 * @param shallowCommits the new shallow commits
+	 *
+	 * @throws IOException the database could not be updated
+	 *
+	 * @since 6.3
+	 */
+	public void setShallowCommits(Set<ObjectId> shallowCommits)
+			throws IOException {
+		if (!shallowCommits.isEmpty()) {
+			throw new UnsupportedOperationException(
+					"Shallow commits expected to be empty."); //$NON-NLS-1$
+		}
+	}
+
+	/**
 	 * Close any resources held by this database.
 	 */
 	@Override
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java
index bf2a78f..df60689 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java
@@ -195,9 +195,6 @@ protected RevCommit getBaseCommit(RevCommit a, RevCommit b, int callDepth)
 			inCore = oldIncore;
 			dircache = oldDircache;
 			workingTreeIterator = oldWTreeIt;
-			toBeCheckedOut.clear();
-			toBeDeleted.clear();
-			modifiedFiles.clear();
 			unmergedPaths.clear();
 			mergeResults.clear();
 			failingPaths.clear();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
index b9ab1d1..e56513d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
@@ -3,7 +3,8 @@
  * Copyright (C) 2010-2012, Matthias Sohn <matthias.sohn@sap.com>
  * Copyright (C) 2012, Research In Motion Limited
  * Copyright (C) 2017, Obeo (mathieu.cartaud@obeo.fr)
- * Copyright (C) 2018, 2022 Thomas Wolf <thomas.wolf@paranor.ch> and others
+ * Copyright (C) 2018, 2022 Thomas Wolf <twolf@apache.org>
+ * Copyright (C) 2022, 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
@@ -20,9 +21,8 @@
 import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_ALGORITHM;
 import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
 
-import java.io.BufferedOutputStream;
+import java.io.Closeable;
 import java.io.File;
-import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -32,12 +32,15 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
-import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
+import java.util.TreeMap;
 
 import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.attributes.Attribute;
 import org.eclipse.jgit.attributes.Attributes;
 import org.eclipse.jgit.diff.DiffAlgorithm;
 import org.eclipse.jgit.diff.DiffAlgorithm.SupportedAlgorithm;
@@ -49,13 +52,12 @@
 import org.eclipse.jgit.dircache.DirCacheBuilder;
 import org.eclipse.jgit.dircache.DirCacheCheckout;
 import org.eclipse.jgit.dircache.DirCacheCheckout.CheckoutMetadata;
+import org.eclipse.jgit.dircache.DirCacheCheckout.StreamSupplier;
 import org.eclipse.jgit.dircache.DirCacheEntry;
 import org.eclipse.jgit.errors.BinaryBlobException;
-import org.eclipse.jgit.errors.CorruptObjectException;
-import org.eclipse.jgit.errors.IncorrectObjectTypeException;
 import org.eclipse.jgit.errors.IndexWriteException;
-import org.eclipse.jgit.errors.MissingObjectException;
 import org.eclipse.jgit.errors.NoWorkTreeException;
+import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.ConfigConstants;
 import org.eclipse.jgit.lib.Constants;
@@ -64,6 +66,7 @@
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectInserter;
 import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.ObjectReader;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.RevTree;
 import org.eclipse.jgit.storage.pack.PackConfig;
@@ -86,6 +89,595 @@
  * A three-way merger performing a content-merge if necessary
  */
 public class ResolveMerger extends ThreeWayMerger {
+
+	/**
+	 * Handles work tree updates on both the checkout and the index.
+	 * <p>
+	 * You should use a single instance for all of your file changes. In case of
+	 * an error, make sure your instance is released, and initiate a new one if
+	 * necessary.
+	 *
+	 * @since 6.3.1
+	 */
+	protected static class WorkTreeUpdater implements Closeable {
+
+		/**
+		 * The result of writing the index changes.
+		 */
+		public static class Result {
+
+			private final List<String> modifiedFiles = new LinkedList<>();
+
+			private final List<String> failedToDelete = new LinkedList<>();
+
+			private ObjectId treeId = null;
+
+			/**
+			 * @return Modified tree ID if any, or null otherwise.
+			 */
+			public ObjectId getTreeId() {
+				return treeId;
+			}
+
+			/**
+			 * @return Files that couldn't be deleted.
+			 */
+			public List<String> getFailedToDelete() {
+				return failedToDelete;
+			}
+
+			/**
+			 * @return Files modified during this operation.
+			 */
+			public List<String> getModifiedFiles() {
+				return modifiedFiles;
+			}
+		}
+
+		Result result = new Result();
+
+		/**
+		 * The repository this handler operates on.
+		 */
+		@Nullable
+		private final Repository repo;
+
+		/**
+		 * Set to true if this operation should work in-memory. The repo's
+		 * dircache and workingtree are not touched by this method. Eventually
+		 * needed files are created as temporary files and a new empty,
+		 * in-memory dircache will be used instead the repo's one. Often used
+		 * for bare repos where the repo doesn't even have a workingtree and
+		 * dircache.
+		 */
+		private final boolean inCore;
+
+		private final ObjectInserter inserter;
+
+		private final ObjectReader reader;
+
+		private DirCache dirCache;
+
+		private boolean implicitDirCache = false;
+
+		/**
+		 * Builder to update the dir cache during this operation.
+		 */
+		private DirCacheBuilder builder;
+
+		/**
+		 * The {@link WorkingTreeOptions} are needed to determine line endings
+		 * for affected files.
+		 */
+		private WorkingTreeOptions workingTreeOptions;
+
+		/**
+		 * The size limit (bytes) which controls a file to be stored in
+		 * {@code Heap} or {@code LocalFile} during the operation.
+		 */
+		private int inCoreFileSizeLimit;
+
+		/**
+		 * If the operation has nothing to do for a file but check it out at the
+		 * end of the operation, it can be added here.
+		 */
+		private final Map<String, DirCacheEntry> toBeCheckedOut = new HashMap<>();
+
+		/**
+		 * Files in this list will be deleted from the local copy at the end of
+		 * the operation.
+		 */
+		private final TreeMap<String, File> toBeDeleted = new TreeMap<>();
+
+		/**
+		 * Keeps {@link CheckoutMetadata} for {@link #checkout()}.
+		 */
+		private Map<String, CheckoutMetadata> checkoutMetadataByPath;
+
+		/**
+		 * Keeps {@link CheckoutMetadata} for {@link #revertModifiedFiles()}.
+		 */
+		private Map<String, CheckoutMetadata> cleanupMetadataByPath;
+
+		/**
+		 * Whether the changes were successfully written.
+		 */
+		private boolean indexChangesWritten;
+
+		/**
+		 * @param repo
+		 *            the {@link Repository}.
+		 * @param dirCache
+		 *            if set, use the provided dir cache. Otherwise, use the
+		 *            default repository one
+		 */
+		private WorkTreeUpdater(Repository repo, DirCache dirCache) {
+			this.repo = repo;
+			this.dirCache = dirCache;
+
+			this.inCore = false;
+			this.inserter = repo.newObjectInserter();
+			this.reader = inserter.newReader();
+			Config config = repo.getConfig();
+			this.workingTreeOptions = config.get(WorkingTreeOptions.KEY);
+			this.inCoreFileSizeLimit = getInCoreFileSizeLimit(config);
+			this.checkoutMetadataByPath = new HashMap<>();
+			this.cleanupMetadataByPath = new HashMap<>();
+		}
+
+		/**
+		 * Creates a new {@link WorkTreeUpdater} for the given repository.
+		 *
+		 * @param repo
+		 *            the {@link Repository}.
+		 * @param dirCache
+		 *            if set, use the provided dir cache. Otherwise, use the
+		 *            default repository one
+		 * @return the {@link WorkTreeUpdater}.
+		 */
+		public static WorkTreeUpdater createWorkTreeUpdater(Repository repo,
+				DirCache dirCache) {
+			return new WorkTreeUpdater(repo, dirCache);
+		}
+
+		/**
+		 * @param repo
+		 *            the {@link Repository}.
+		 * @param dirCache
+		 *            if set, use the provided dir cache. Otherwise, creates a
+		 *            new one
+		 * @param oi
+		 *            to use for writing the modified objects with.
+		 */
+		private WorkTreeUpdater(Repository repo, DirCache dirCache,
+				ObjectInserter oi) {
+			this.repo = repo;
+			this.dirCache = dirCache;
+			this.inserter = oi;
+
+			this.inCore = true;
+			this.reader = oi.newReader();
+			if (repo != null) {
+				this.inCoreFileSizeLimit = getInCoreFileSizeLimit(
+						repo.getConfig());
+			}
+		}
+
+		/**
+		 * Creates a new {@link WorkTreeUpdater} that works in memory only.
+		 *
+		 * @param repo
+		 *            the {@link Repository}.
+		 * @param dirCache
+		 *            if set, use the provided dir cache. Otherwise, creates a
+		 *            new one
+		 * @param oi
+		 *            to use for writing the modified objects with.
+		 * @return the {@link WorkTreeUpdater}
+		 */
+		public static WorkTreeUpdater createInCoreWorkTreeUpdater(
+				Repository repo, DirCache dirCache, ObjectInserter oi) {
+			return new WorkTreeUpdater(repo, dirCache, oi);
+		}
+
+		private static int getInCoreFileSizeLimit(Config config) {
+			return config.getInt(ConfigConstants.CONFIG_MERGE_SECTION,
+					ConfigConstants.CONFIG_KEY_IN_CORE_LIMIT, 10 << 20);
+		}
+
+		/**
+		 * Gets the size limit for in-core files in this config.
+		 *
+		 * @return the size
+		 */
+		public int getInCoreFileSizeLimit() {
+			return inCoreFileSizeLimit;
+		}
+
+		/**
+		 * Gets dir cache for the repo. Locked if not inCore.
+		 *
+		 * @return the result dir cache
+		 * @throws IOException
+		 *             is case the dir cache cannot be read
+		 */
+		public DirCache getLockedDirCache() throws IOException {
+			if (dirCache == null) {
+				implicitDirCache = true;
+				if (inCore) {
+					dirCache = DirCache.newInCore();
+				} else {
+					dirCache = nonNullRepo().lockDirCache();
+				}
+			}
+			if (builder == null) {
+				builder = dirCache.builder();
+			}
+			return dirCache;
+		}
+
+		/**
+		 * Creates a {@link DirCacheBuildIterator} for the builder of this
+		 * {@link WorkTreeUpdater}.
+		 *
+		 * @return the {@link DirCacheBuildIterator}
+		 */
+		public DirCacheBuildIterator createDirCacheBuildIterator() {
+			return new DirCacheBuildIterator(builder);
+		}
+
+		/**
+		 * Writes the changes to the working tree (but not to the index).
+		 *
+		 * @param shouldCheckoutTheirs
+		 *            before committing the changes
+		 * @throws IOException
+		 *             if any of the writes fail
+		 */
+		public void writeWorkTreeChanges(boolean shouldCheckoutTheirs)
+				throws IOException {
+			handleDeletedFiles();
+
+			if (inCore) {
+				builder.finish();
+				return;
+			}
+			if (shouldCheckoutTheirs) {
+				// No problem found. The only thing left to be done is to
+				// check out all files from "theirs" which have been selected to
+				// go into the new index.
+				checkout();
+			}
+
+			// All content operations are successfully done. If we can now write
+			// the
+			// new index we are on quite safe ground. Even if the checkout of
+			// files coming from "theirs" fails the user can work around such
+			// failures by checking out the index again.
+			if (!builder.commit()) {
+				revertModifiedFiles();
+				throw new IndexWriteException();
+			}
+		}
+
+		/**
+		 * Writes the changes to the index.
+		 *
+		 * @return the {@link Result} of the operation.
+		 * @throws IOException
+		 *             if any of the writes fail
+		 */
+		public Result writeIndexChanges() throws IOException {
+			result.treeId = getLockedDirCache().writeTree(inserter);
+			indexChangesWritten = true;
+			return result;
+		}
+
+		/**
+		 * Adds a {@link DirCacheEntry} for direct checkout and remembers its
+		 * {@link CheckoutMetadata}.
+		 *
+		 * @param path
+		 *            of the entry
+		 * @param entry
+		 *            to add
+		 * @param cleanupStreamType
+		 *            to use for the cleanup metadata
+		 * @param cleanupSmudgeCommand
+		 *            to use for the cleanup metadata
+		 * @param checkoutStreamType
+		 *            to use for the checkout metadata
+		 * @param checkoutSmudgeCommand
+		 *            to use for the checkout metadata
+		 */
+		public void addToCheckout(String path, DirCacheEntry entry,
+				EolStreamType cleanupStreamType, String cleanupSmudgeCommand,
+				EolStreamType checkoutStreamType,
+				String checkoutSmudgeCommand) {
+			if (entry != null) {
+				// In some cases, we just want to add the metadata.
+				toBeCheckedOut.put(path, entry);
+			}
+			addCheckoutMetadata(cleanupMetadataByPath, path, cleanupStreamType,
+					cleanupSmudgeCommand);
+			addCheckoutMetadata(checkoutMetadataByPath, path,
+					checkoutStreamType, checkoutSmudgeCommand);
+		}
+
+		/**
+		 * Gets a map which maps the paths of files which have to be checked out
+		 * because the operation created new fully-merged content for this file
+		 * into the index.
+		 * <p>
+		 * This means: the operation wrote a new stage 0 entry for this path.
+		 * </p>
+		 *
+		 * @return the map
+		 */
+		public Map<String, DirCacheEntry> getToBeCheckedOut() {
+			return toBeCheckedOut;
+		}
+
+		/**
+		 * Remembers the given file to be deleted.
+		 * <p>
+		 * Note the actual deletion is only done in
+		 * {@link #writeWorkTreeChanges}.
+		 *
+		 * @param path
+		 *            of the file to be deleted
+		 * @param file
+		 *            to be deleted
+		 * @param streamType
+		 *            to use for cleanup metadata
+		 * @param smudgeCommand
+		 *            to use for cleanup metadata
+		 */
+		public void deleteFile(String path, File file, EolStreamType streamType,
+				String smudgeCommand) {
+			toBeDeleted.put(path, file);
+			if (file != null && file.isFile()) {
+				addCheckoutMetadata(cleanupMetadataByPath, path, streamType,
+						smudgeCommand);
+			}
+		}
+
+		/**
+		 * Remembers the {@link CheckoutMetadata} for the given path; it may be
+		 * needed in {@link #checkout()} or in {@link #revertModifiedFiles()}.
+		 *
+		 * @param map
+		 *            to add the metadata to
+		 * @param path
+		 *            of the current node
+		 * @param streamType
+		 *            to use for the metadata
+		 * @param smudgeCommand
+		 *            to use for the metadata
+		 */
+		private void addCheckoutMetadata(Map<String, CheckoutMetadata> map,
+				String path, EolStreamType streamType, String smudgeCommand) {
+			if (inCore || map == null) {
+				return;
+			}
+			map.put(path, new CheckoutMetadata(streamType, smudgeCommand));
+		}
+
+		/**
+		 * Detects if CRLF conversion has been configured.
+		 * <p>
+		 * </p>
+		 * See {@link EolStreamTypeUtil#detectStreamType} for more info.
+		 *
+		 * @param attributes
+		 *            of the file for which the type is to be detected
+		 * @return the detected type
+		 */
+		public EolStreamType detectCheckoutStreamType(Attributes attributes) {
+			if (inCore) {
+				return null;
+			}
+			return EolStreamTypeUtil.detectStreamType(OperationType.CHECKOUT_OP,
+					workingTreeOptions, attributes);
+		}
+
+		private void handleDeletedFiles() {
+			// Iterate in reverse so that "folder/file" is deleted before
+			// "folder". Otherwise, this could result in a failing path because
+			// of a non-empty directory, for which delete() would fail.
+			for (String path : toBeDeleted.descendingKeySet()) {
+				File file = inCore ? null : toBeDeleted.get(path);
+				if (file != null && !file.delete()) {
+					if (!file.isDirectory()) {
+						result.failedToDelete.add(path);
+					}
+				}
+			}
+		}
+
+		/**
+		 * Marks the given path as modified in the operation.
+		 *
+		 * @param path
+		 *            to mark as modified
+		 */
+		public void markAsModified(String path) {
+			result.modifiedFiles.add(path);
+		}
+
+		/**
+		 * Gets the list of files which were modified in this operation.
+		 *
+		 * @return the list
+		 */
+		public List<String> getModifiedFiles() {
+			return result.modifiedFiles;
+		}
+
+		private void checkout() throws NoWorkTreeException, IOException {
+			for (Map.Entry<String, DirCacheEntry> entry : toBeCheckedOut
+					.entrySet()) {
+				DirCacheEntry dirCacheEntry = entry.getValue();
+				if (dirCacheEntry.getFileMode() == FileMode.GITLINK) {
+					new File(nonNullRepo().getWorkTree(), entry.getKey())
+							.mkdirs();
+				} else {
+					DirCacheCheckout.checkoutEntry(repo, dirCacheEntry, reader,
+							false, checkoutMetadataByPath.get(entry.getKey()),
+							workingTreeOptions);
+					result.modifiedFiles.add(entry.getKey());
+				}
+			}
+		}
+
+		/**
+		 * Reverts any uncommitted changes in the worktree. We know that for all
+		 * modified files the old content was in the old index and the index
+		 * contained only stage 0. In case of inCore operation just clear the
+		 * history of modified files.
+		 *
+		 * @throws IOException
+		 *             in case the cleaning up failed
+		 */
+		public void revertModifiedFiles() throws IOException {
+			if (inCore) {
+				result.modifiedFiles.clear();
+				return;
+			}
+			if (indexChangesWritten) {
+				return;
+			}
+			for (String path : result.modifiedFiles) {
+				DirCacheEntry entry = dirCache.getEntry(path);
+				if (entry != null) {
+					DirCacheCheckout.checkoutEntry(repo, entry, reader, false,
+							cleanupMetadataByPath.get(path),
+							workingTreeOptions);
+				}
+			}
+		}
+
+		@Override
+		public void close() throws IOException {
+			if (implicitDirCache) {
+				dirCache.unlock();
+			}
+		}
+
+		/**
+		 * Updates the file in the checkout with the given content.
+		 *
+		 * @param inputStream
+		 *            the content to be updated
+		 * @param streamType
+		 *            for parsing the content
+		 * @param smudgeCommand
+		 *            for formatting the content
+		 * @param path
+		 *            of the file to be updated
+		 * @param file
+		 *            to be updated
+		 * @throws IOException
+		 *             if the file cannot be updated
+		 */
+		public void updateFileWithContent(StreamSupplier inputStream,
+				EolStreamType streamType, String smudgeCommand, String path,
+				File file) throws IOException {
+			if (inCore) {
+				return;
+			}
+			CheckoutMetadata metadata = new CheckoutMetadata(streamType,
+					smudgeCommand);
+
+			try (OutputStream outputStream = new FileOutputStream(file)) {
+				DirCacheCheckout.getContent(repo, path, metadata, inputStream,
+						workingTreeOptions, outputStream);
+			}
+		}
+
+		/**
+		 * Creates a path with the given content, and adds it to the specified
+		 * stage to the index builder.
+		 *
+		 * @param input
+		 *            the content to be updated
+		 * @param path
+		 *            of the file to be updated
+		 * @param fileMode
+		 *            of the modified file
+		 * @param entryStage
+		 *            of the new entry
+		 * @param lastModified
+		 *            instant of the modified file
+		 * @param len
+		 *            of the content
+		 * @param lfsAttribute
+		 *            for checking for LFS enablement
+		 * @return the entry which was added to the index
+		 * @throws IOException
+		 *             if inserting the content fails
+		 */
+		public DirCacheEntry insertToIndex(InputStream input, byte[] path,
+				FileMode fileMode, int entryStage, Instant lastModified,
+				int len, Attribute lfsAttribute) throws IOException {
+			return addExistingToIndex(insertResult(input, lfsAttribute, len),
+					path, fileMode, entryStage, lastModified, len);
+		}
+
+		/**
+		 * Adds a path with the specified stage to the index builder.
+		 *
+		 * @param objectId
+		 *            of the existing object to add
+		 * @param path
+		 *            of the modified file
+		 * @param fileMode
+		 *            of the modified file
+		 * @param entryStage
+		 *            of the new entry
+		 * @param lastModified
+		 *            instant of the modified file
+		 * @param len
+		 *            of the modified file content
+		 * @return the entry which was added to the index
+		 */
+		public DirCacheEntry addExistingToIndex(ObjectId objectId, byte[] path,
+				FileMode fileMode, int entryStage, Instant lastModified,
+				int len) {
+			DirCacheEntry dce = new DirCacheEntry(path, entryStage);
+			dce.setFileMode(fileMode);
+			if (lastModified != null) {
+				dce.setLastModified(lastModified);
+			}
+			dce.setLength(inCore ? 0 : len);
+			dce.setObjectId(objectId);
+			builder.add(dce);
+			return dce;
+		}
+
+		private ObjectId insertResult(InputStream input, Attribute lfsAttribute,
+				long length) throws IOException {
+			try (LfsInputStream is = LfsFactory.getInstance()
+					.applyCleanFilter(repo, input, length, lfsAttribute)) {
+				return inserter.insert(OBJ_BLOB, is.getLength(), is);
+			}
+		}
+
+		/**
+		 * Gets the non-null repository instance of this
+		 * {@link WorkTreeUpdater}.
+		 *
+		 * @return non-null repository instance
+		 * @throws NullPointerException
+		 *             if the handler was constructed without a repository.
+		 */
+		@NonNull
+		private Repository nonNullRepo() throws NullPointerException {
+			return Objects.requireNonNull(repo,
+					() -> JGitText.get().repositoryIsRequired);
+		}
+	}
+
 	/**
 	 * If the merge fails (means: not stopped because of unresolved conflicts)
 	 * this enum is used to explain why it failed
@@ -149,11 +741,11 @@ public enum MergeFailureReason {
 	protected static final int T_FILE = 4;
 
 	/**
-	 * Builder to update the cache during this merge.
+	 * Handler for repository I/O actions.
 	 *
-	 * @since 3.4
+	 * @since 6.3
 	 */
-	protected DirCacheBuilder builder;
+	protected WorkTreeUpdater workTreeUpdater;
 
 	/**
 	 * merge result as tree
@@ -163,6 +755,11 @@ public enum MergeFailureReason {
 	protected ObjectId resultTree;
 
 	/**
+	 * Files modified during this operation. Note this list is only updated after a successful write.
+	 */
+	protected List<String> modifiedFiles = new ArrayList<>();
+
+	/**
 	 * Paths that could not be merged by this merger because of an unsolvable
 	 * conflict.
 	 *
@@ -171,29 +768,6 @@ public enum MergeFailureReason {
 	protected List<String> unmergedPaths = new ArrayList<>();
 
 	/**
-	 * Files modified during this merge operation.
-	 *
-	 * @since 3.4
-	 */
-	protected List<String> modifiedFiles = new LinkedList<>();
-
-	/**
-	 * If the merger has nothing to do for a file but check it out at the end of
-	 * the operation, it can be added here.
-	 *
-	 * @since 3.4
-	 */
-	protected Map<String, DirCacheEntry> toBeCheckedOut = new HashMap<>();
-
-	/**
-	 * Paths in this list will be deleted from the local copy at the end of the
-	 * operation.
-	 *
-	 * @since 3.4
-	 */
-	protected List<String> toBeDeleted = new ArrayList<>();
-
-	/**
 	 * Low-level textual merge results. Will be passed on to the callers in case
 	 * of conflicts.
 	 *
@@ -227,15 +801,6 @@ public enum MergeFailureReason {
 	protected boolean inCore;
 
 	/**
-	 * Set to true if this merger should use the default dircache of the
-	 * repository and should handle locking and unlocking of the dircache. If
-	 * this merger should work in-core or if an explicit dircache was specified
-	 * during construction then this field is set to false.
-	 * @since 3.0
-	 */
-	protected boolean implicitDirCache;
-
-	/**
 	 * Directory cache
 	 * @since 3.0
 	 */
@@ -255,36 +820,12 @@ public enum MergeFailureReason {
 	protected MergeAlgorithm mergeAlgorithm;
 
 	/**
-	 * The {@link WorkingTreeOptions} are needed to determine line endings for
-	 * merged files.
-	 *
-	 * @since 4.11
-	 */
-	protected WorkingTreeOptions workingTreeOptions;
-
-	/**
-	 * The size limit (bytes) which controls a file to be stored in {@code Heap}
-	 * or {@code LocalFile} during the merge.
-	 */
-	private int inCoreLimit;
-
-	/**
 	 * The {@link ContentMergeStrategy} to use for "resolve" and "recursive"
 	 * merges.
 	 */
 	@NonNull
 	private ContentMergeStrategy contentStrategy = ContentMergeStrategy.CONFLICT;
 
-	/**
-	 * Keeps {@link CheckoutMetadata} for {@link #checkout()}.
-	 */
-	private Map<String, CheckoutMetadata> checkoutMetadata;
-
-	/**
-	 * Keeps {@link CheckoutMetadata} for {@link #cleanUp()}.
-	 */
-	private Map<String, CheckoutMetadata> cleanupMetadata;
-
 	private static MergeAlgorithm getMergeAlgorithm(Config config) {
 		SupportedAlgorithm diffAlg = config.getEnum(
 				CONFIG_DIFF_SECTION, null, CONFIG_KEY_ALGORITHM,
@@ -292,13 +833,8 @@ private static MergeAlgorithm getMergeAlgorithm(Config config) {
 		return new MergeAlgorithm(DiffAlgorithm.getAlgorithm(diffAlg));
 	}
 
-	private static int getInCoreLimit(Config config) {
-		return config.getInt(
-				ConfigConstants.CONFIG_MERGE_SECTION, ConfigConstants.CONFIG_KEY_IN_CORE_LIMIT, 10 << 20);
-	}
-
 	private static String[] defaultCommitNames() {
-		return new String[] { "BASE", "OURS", "THEIRS" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		return new String[]{"BASE", "OURS", "THEIRS"}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 	}
 
 	private static final Attributes NO_ATTRIBUTES = new Attributes();
@@ -315,17 +851,8 @@ protected ResolveMerger(Repository local, boolean inCore) {
 		super(local);
 		Config config = local.getConfig();
 		mergeAlgorithm = getMergeAlgorithm(config);
-		inCoreLimit = getInCoreLimit(config);
 		commitNames = defaultCommitNames();
 		this.inCore = inCore;
-
-		if (inCore) {
-			implicitDirCache = false;
-			dircache = DirCache.newInCore();
-		} else {
-			implicitDirCache = true;
-			workingTreeOptions = local.getConfig().get(WorkingTreeOptions.KEY);
-		}
 	}
 
 	/**
@@ -352,8 +879,6 @@ protected ResolveMerger(ObjectInserter inserter, Config config) {
 		mergeAlgorithm = getMergeAlgorithm(config);
 		commitNames = defaultCommitNames();
 		inCore = true;
-		implicitDirCache = false;
-		dircache = DirCache.newInCore();
 	}
 
 	/**
@@ -382,81 +907,8 @@ public void setContentMergeStrategy(ContentMergeStrategy strategy) {
 	/** {@inheritDoc} */
 	@Override
 	protected boolean mergeImpl() throws IOException {
-		if (implicitDirCache) {
-			dircache = nonNullRepo().lockDirCache();
-		}
-		if (!inCore) {
-			checkoutMetadata = new HashMap<>();
-			cleanupMetadata = new HashMap<>();
-		}
-		try {
-			return mergeTrees(mergeBase(), sourceTrees[0], sourceTrees[1],
-					false);
-		} finally {
-			checkoutMetadata = null;
-			cleanupMetadata = null;
-			if (implicitDirCache) {
-				dircache.unlock();
-			}
-		}
-	}
-
-	private void checkout() throws NoWorkTreeException, IOException {
-		// Iterate in reverse so that "folder/file" is deleted before
-		// "folder". Otherwise this could result in a failing path because
-		// of a non-empty directory, for which delete() would fail.
-		for (int i = toBeDeleted.size() - 1; i >= 0; i--) {
-			String fileName = toBeDeleted.get(i);
-			File f = new File(nonNullRepo().getWorkTree(), fileName);
-			if (!f.delete())
-				if (!f.isDirectory())
-					failingPaths.put(fileName,
-							MergeFailureReason.COULD_NOT_DELETE);
-			modifiedFiles.add(fileName);
-		}
-		for (Map.Entry<String, DirCacheEntry> entry : toBeCheckedOut
-				.entrySet()) {
-			DirCacheEntry cacheEntry = entry.getValue();
-			if (cacheEntry.getFileMode() == FileMode.GITLINK) {
-				new File(nonNullRepo().getWorkTree(), entry.getKey()).mkdirs();
-			} else {
-				DirCacheCheckout.checkoutEntry(db, cacheEntry, reader, false,
-						checkoutMetadata.get(entry.getKey()));
-				modifiedFiles.add(entry.getKey());
-			}
-		}
-	}
-
-	/**
-	 * Reverts the worktree after an unsuccessful merge. We know that for all
-	 * modified files the old content was in the old index and the index
-	 * contained only stage 0. In case if inCore operation just clear the
-	 * history of modified files.
-	 *
-	 * @throws java.io.IOException
-	 * @throws org.eclipse.jgit.errors.CorruptObjectException
-	 * @throws org.eclipse.jgit.errors.NoWorkTreeException
-	 * @since 3.4
-	 */
-	protected void cleanUp() throws NoWorkTreeException,
-			CorruptObjectException,
-			IOException {
-		if (inCore) {
-			modifiedFiles.clear();
-			return;
-		}
-
-		DirCache dc = nonNullRepo().readDirCache();
-		Iterator<String> mpathsIt=modifiedFiles.iterator();
-		while(mpathsIt.hasNext()) {
-			String mpath = mpathsIt.next();
-			DirCacheEntry entry = dc.getEntry(mpath);
-			if (entry != null) {
-				DirCacheCheckout.checkoutEntry(db, entry, reader, false,
-						cleanupMetadata.get(mpath));
-			}
-			mpathsIt.remove();
-		}
+		return mergeTrees(mergeBase(), sourceTrees[0], sourceTrees[1],
+				false);
 	}
 
 	/**
@@ -472,18 +924,33 @@ protected void cleanUp() throws NoWorkTreeException,
 	private DirCacheEntry add(byte[] path, CanonicalTreeParser p, int stage,
 			Instant lastMod, long len) {
 		if (p != null && !p.getEntryFileMode().equals(FileMode.TREE)) {
-			DirCacheEntry e = new DirCacheEntry(path, stage);
-			e.setFileMode(p.getEntryFileMode());
-			e.setObjectId(p.getEntryObjectId());
-			e.setLastModified(lastMod);
-			e.setLength(len);
-			builder.add(e);
-			return e;
+			return workTreeUpdater.addExistingToIndex(p.getEntryObjectId(), path,
+					p.getEntryFileMode(), stage,
+					lastMod, (int) len);
 		}
 		return null;
 	}
 
 	/**
+	 * Adds the conflict stages for the current path of {@link #tw} to the index
+	 * builder and returns the "theirs" stage; if present.
+	 *
+	 * @param base
+	 *            of the conflict
+	 * @param ours
+	 *            of the conflict
+	 * @param theirs
+	 *            of the conflict
+	 * @return the {@link DirCacheEntry} for the "theirs" stage, or {@code null}
+	 */
+	private DirCacheEntry addConflict(CanonicalTreeParser base,
+			CanonicalTreeParser ours, CanonicalTreeParser theirs) {
+		add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, EPOCH, 0);
+		add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, EPOCH, 0);
+		return add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, EPOCH, 0);
+	}
+
+	/**
 	 * adds a entry to the index builder which is a copy of the specified
 	 * DirCacheEntry
 	 *
@@ -493,41 +960,8 @@ private DirCacheEntry add(byte[] path, CanonicalTreeParser p, int stage,
 	 * @return the entry which was added to the index
 	 */
 	private DirCacheEntry keep(DirCacheEntry e) {
-		DirCacheEntry newEntry = new DirCacheEntry(e.getRawPath(),
-				e.getStage());
-		newEntry.setFileMode(e.getFileMode());
-		newEntry.setObjectId(e.getObjectId());
-		newEntry.setLastModified(e.getLastModifiedInstant());
-		newEntry.setLength(e.getLength());
-		builder.add(newEntry);
-		return newEntry;
-	}
-
-	/**
-	 * Remembers the {@link CheckoutMetadata} for the given path; it may be
-	 * needed in {@link #checkout()} or in {@link #cleanUp()}.
-	 *
-	 * @param map
-	 *            to add the metadata to
-	 * @param path
-	 *            of the current node
-	 * @param attributes
-	 *            to use for determining the metadata
-	 * @throws IOException
-	 *             if the smudge filter cannot be determined
-	 * @since 6.1
-	 */
-	protected void addCheckoutMetadata(Map<String, CheckoutMetadata> map,
-			String path, Attributes attributes)
-			throws IOException {
-		if (map != null) {
-			EolStreamType eol = EolStreamTypeUtil.detectStreamType(
-					OperationType.CHECKOUT_OP, workingTreeOptions,
-					attributes);
-			CheckoutMetadata data = new CheckoutMetadata(eol,
-					tw.getSmudgeCommand(attributes));
-			map.put(path, data);
-		}
+		return workTreeUpdater.addExistingToIndex(e.getObjectId(), e.getRawPath(), e.getFileMode(),
+				e.getStage(), e.getLastModifiedInstant(), e.getLength());
 	}
 
 	/**
@@ -547,14 +981,17 @@ protected void addCheckoutMetadata(Map<String, CheckoutMetadata> map,
 	protected void addToCheckout(String path, DirCacheEntry entry,
 			Attributes[] attributes)
 			throws IOException {
-		toBeCheckedOut.put(path, entry);
-		addCheckoutMetadata(cleanupMetadata, path, attributes[T_OURS]);
-		addCheckoutMetadata(checkoutMetadata, path, attributes[T_THEIRS]);
+		EolStreamType cleanupStreamType = workTreeUpdater.detectCheckoutStreamType(attributes[T_OURS]);
+		String cleanupSmudgeCommand = tw.getSmudgeCommand(attributes[T_OURS]);
+		EolStreamType checkoutStreamType = workTreeUpdater.detectCheckoutStreamType(attributes[T_THEIRS]);
+		String checkoutSmudgeCommand = tw.getSmudgeCommand(attributes[T_THEIRS]);
+		workTreeUpdater.addToCheckout(path, entry, cleanupStreamType, cleanupSmudgeCommand,
+				checkoutStreamType, checkoutSmudgeCommand);
 	}
 
 	/**
 	 * Remember a path for deletion, and remember its {@link CheckoutMetadata}
-	 * in case it has to be restored in {@link #cleanUp()}.
+	 * in case it has to be restored in the cleanUp.
 	 *
 	 * @param path
 	 *            of the entry
@@ -568,10 +1005,13 @@ protected void addToCheckout(String path, DirCacheEntry entry,
 	 */
 	protected void addDeletion(String path, boolean isFile,
 			Attributes attributes) throws IOException {
-		toBeDeleted.add(path);
-		if (isFile) {
-			addCheckoutMetadata(cleanupMetadata, path, attributes);
-		}
+		if (db == null || nonNullRepo().isBare() || !isFile)
+			return;
+
+		File file = new File(nonNullRepo().getWorkTree(), path);
+		EolStreamType streamType = workTreeUpdater.detectCheckoutStreamType(attributes);
+		String smudgeCommand = tw.getSmudgeCommand(attributes);
+		workTreeUpdater.deleteFile(path, file, streamType, smudgeCommand);
 	}
 
 	/**
@@ -615,9 +1055,6 @@ protected void addDeletion(String path, boolean isFile,
 	 * @return <code>false</code> if the merge will fail because the index entry
 	 *         didn't match ours or the working-dir file was dirty and a
 	 *         conflict occurred
-	 * @throws org.eclipse.jgit.errors.MissingObjectException
-	 * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
-	 * @throws org.eclipse.jgit.errors.CorruptObjectException
 	 * @throws java.io.IOException
 	 * @since 6.1
 	 */
@@ -625,20 +1062,21 @@ protected boolean processEntry(CanonicalTreeParser base,
 			CanonicalTreeParser ours, CanonicalTreeParser theirs,
 			DirCacheBuildIterator index, WorkingTreeIterator work,
 			boolean ignoreConflicts, Attributes[] attributes)
-			throws MissingObjectException, IncorrectObjectTypeException,
-			CorruptObjectException, IOException {
+			throws IOException {
 		enterSubtree = true;
 		final int modeO = tw.getRawMode(T_OURS);
 		final int modeT = tw.getRawMode(T_THEIRS);
 		final int modeB = tw.getRawMode(T_BASE);
 		boolean gitLinkMerging = isGitLink(modeO) || isGitLink(modeT)
 				|| isGitLink(modeB);
-		if (modeO == 0 && modeT == 0 && modeB == 0)
+		if (modeO == 0 && modeT == 0 && modeB == 0) {
 			// File is either untracked or new, staged but uncommitted
 			return true;
+		}
 
-		if (isIndexDirty())
+		if (isIndexDirty()) {
 			return false;
+		}
 
 		DirCacheEntry ourDce = null;
 
@@ -693,9 +1131,7 @@ protected boolean processEntry(CanonicalTreeParser base,
 				// length.
 				// This path can be skipped on ignoreConflicts, so the caller
 				// could use virtual commit.
-				add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, EPOCH, 0);
-				add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, EPOCH, 0);
-				add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, EPOCH, 0);
+				addConflict(base, ours, theirs);
 				unmergedPaths.add(tw.getPathString());
 				mergeResults.put(tw.getPathString(),
 						new MergeResult<>(Collections.emptyList()));
@@ -706,8 +1142,9 @@ protected boolean processEntry(CanonicalTreeParser base,
 		if (modeB == modeT && tw.idEqual(T_BASE, T_THEIRS)) {
 			// THEIRS was not changed compared to BASE. All changes must be in
 			// OURS. OURS is chosen. We can keep the existing entry.
-			if (ourDce != null)
+			if (ourDce != null) {
 				keep(ourDce);
+			}
 			// no checkout needed!
 			return true;
 		}
@@ -717,8 +1154,9 @@ protected boolean processEntry(CanonicalTreeParser base,
 			// THEIRS. THEIRS is chosen.
 
 			// Check worktree before checking out THEIRS
-			if (isWorktreeDirty(work, ourDce))
+			if (isWorktreeDirty(work, ourDce)) {
 				return false;
+			}
 			if (nonTree(modeT)) {
 				// we know about length and lastMod only after we have written
 				// the new content.
@@ -759,12 +1197,15 @@ protected boolean processEntry(CanonicalTreeParser base,
 					enterSubtree = false;
 					return true;
 				}
-				if (nonTree(modeB))
+				if (nonTree(modeB)) {
 					add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, EPOCH, 0);
-				if (nonTree(modeO))
+				}
+				if (nonTree(modeO)) {
 					add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, EPOCH, 0);
-				if (nonTree(modeT))
+				}
+				if (nonTree(modeT)) {
 					add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, EPOCH, 0);
+				}
 				unmergedPaths.add(tw.getPathString());
 				enterSubtree = false;
 				return true;
@@ -774,8 +1215,9 @@ protected boolean processEntry(CanonicalTreeParser base,
 			// tells us we are in a subtree because of index or working-dir).
 			// If they are both folders no content-merge is required - we can
 			// return here.
-			if (!nonTree(modeO))
+			if (!nonTree(modeO)) {
 				return true;
+			}
 
 			// ours and theirs are both files, just fall out of the if block
 			// and do the content merge
@@ -794,9 +1236,7 @@ protected boolean processEntry(CanonicalTreeParser base,
 				add(tw.getRawPath(), ours, DirCacheEntry.STAGE_0, EPOCH, 0);
 				return true;
 			} else if (gitLinkMerging) {
-				add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, EPOCH, 0);
-				add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, EPOCH, 0);
-				add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, EPOCH, 0);
+				addConflict(base, ours, theirs);
 				MergeResult<SubmoduleConflict> result = createGitLinksMergeResult(
 						base, ours, theirs);
 				result.setContainsConflicts(true);
@@ -806,20 +1246,18 @@ protected boolean processEntry(CanonicalTreeParser base,
 			} else if (!attributes[T_OURS].canBeContentMerged()) {
 				// File marked as binary
 				switch (getContentMergeStrategy()) {
-				case OURS:
-					keep(ourDce);
-					return true;
-				case THEIRS:
-					DirCacheEntry theirEntry = add(tw.getRawPath(), theirs,
-							DirCacheEntry.STAGE_0, EPOCH, 0);
-					addToCheckout(tw.getPathString(), theirEntry, attributes);
-					return true;
-				default:
-					break;
+					case OURS:
+						keep(ourDce);
+						return true;
+					case THEIRS:
+						DirCacheEntry theirEntry = add(tw.getRawPath(), theirs,
+								DirCacheEntry.STAGE_0, EPOCH, 0);
+						addToCheckout(tw.getPathString(), theirEntry, attributes);
+						return true;
+					default:
+						break;
 				}
-				add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, EPOCH, 0);
-				add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, EPOCH, 0);
-				add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, EPOCH, 0);
+				addConflict(base, ours, theirs);
 
 				// attribute merge issues are conflicts but not failures
 				unmergedPaths.add(tw.getPathString());
@@ -832,18 +1270,27 @@ protected boolean processEntry(CanonicalTreeParser base,
 			}
 
 			MergeResult<RawText> result = null;
-			try {
-				result = contentMerge(base, ours, theirs, attributes,
-						getContentMergeStrategy());
-			} catch (BinaryBlobException e) {
+			boolean hasSymlink = FileMode.SYMLINK.equals(modeO)
+					|| FileMode.SYMLINK.equals(modeT);
+			if (!hasSymlink) {
+				try {
+					result = contentMerge(base, ours, theirs, attributes,
+							getContentMergeStrategy());
+				} catch (BinaryBlobException e) {
+					// result == null
+				}
+			}
+			if (result == null) {
 				switch (getContentMergeStrategy()) {
 				case OURS:
 					keep(ourDce);
 					return true;
 				case THEIRS:
-					DirCacheEntry theirEntry = add(tw.getRawPath(), theirs,
+					DirCacheEntry e = add(tw.getRawPath(), theirs,
 							DirCacheEntry.STAGE_0, EPOCH, 0);
-					addToCheckout(tw.getPathString(), theirEntry, attributes);
+					if (e != null) {
+						addToCheckout(tw.getPathString(), e, attributes);
+					}
 					return true;
 				default:
 					result = new MergeResult<>(Collections.emptyList());
@@ -854,16 +1301,36 @@ protected boolean processEntry(CanonicalTreeParser base,
 			if (ignoreConflicts) {
 				result.setContainsConflicts(false);
 			}
-			updateIndex(base, ours, theirs, result, attributes[T_OURS]);
 			String currentPath = tw.getPathString();
+			if (hasSymlink) {
+				if (ignoreConflicts) {
+					if (((modeT & FileMode.TYPE_MASK) == FileMode.TYPE_FILE)) {
+						DirCacheEntry e = add(tw.getRawPath(), theirs,
+								DirCacheEntry.STAGE_0, EPOCH, 0);
+						addToCheckout(currentPath, e, attributes);
+					} else {
+						keep(ourDce);
+					}
+				} else {
+					// Record the conflict
+					DirCacheEntry e = addConflict(base, ours, theirs);
+					mergeResults.put(currentPath, result);
+					// If theirs is a file, check it out. In link/file
+					// conflicts, C git prefers the file.
+					if (((modeT & FileMode.TYPE_MASK) == FileMode.TYPE_FILE)
+							&& e != null) {
+						addToCheckout(currentPath, e, attributes);
+					}
+				}
+			} else {
+				updateIndex(base, ours, theirs, result, attributes[T_OURS]);
+			}
 			if (result.containsConflicts() && !ignoreConflicts) {
 				unmergedPaths.add(currentPath);
 			}
-			modifiedFiles.add(currentPath);
-			addCheckoutMetadata(cleanupMetadata, currentPath,
-					attributes[T_OURS]);
-			addCheckoutMetadata(checkoutMetadata, currentPath,
-					attributes[T_THEIRS]);
+			workTreeUpdater.markAsModified(currentPath);
+			// Entry is null - only adds the metadata.
+			addToCheckout(currentPath, null, attributes);
 		} else if (modeO != modeT) {
 			// OURS or THEIRS has been deleted
 			if (((modeO != 0 && !tw.idEqual(T_BASE, T_OURS)) || (modeT != 0 && !tw
@@ -871,40 +1338,58 @@ protected boolean processEntry(CanonicalTreeParser base,
 				if (gitLinkMerging && ignoreConflicts) {
 					add(tw.getRawPath(), ours, DirCacheEntry.STAGE_0, EPOCH, 0);
 				} else if (gitLinkMerging) {
-					add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, EPOCH, 0);
-					add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, EPOCH, 0);
-					add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, EPOCH, 0);
+					addConflict(base, ours, theirs);
 					MergeResult<SubmoduleConflict> result = createGitLinksMergeResult(
 							base, ours, theirs);
 					result.setContainsConflicts(true);
 					mergeResults.put(tw.getPathString(), result);
 					unmergedPaths.add(tw.getPathString());
 				} else {
+					boolean isSymLink = ((modeO | modeT)
+							& FileMode.TYPE_MASK) == FileMode.TYPE_SYMLINK;
 					// Content merge strategy does not apply to delete-modify
 					// conflicts!
 					MergeResult<RawText> result;
-					try {
-						result = contentMerge(base, ours, theirs, attributes,
-								ContentMergeStrategy.CONFLICT);
-					} catch (BinaryBlobException e) {
+					if (isSymLink) {
+						// No need to do a content merge
 						result = new MergeResult<>(Collections.emptyList());
 						result.setContainsConflicts(true);
+					} else {
+						try {
+							result = contentMerge(base, ours, theirs,
+									attributes, ContentMergeStrategy.CONFLICT);
+						} catch (BinaryBlobException e) {
+							result = new MergeResult<>(Collections.emptyList());
+							result.setContainsConflicts(true);
+						}
 					}
 					if (ignoreConflicts) {
-						// In case a conflict is detected the working tree file
-						// is again filled with new content (containing conflict
-						// markers). But also stage 0 of the index is filled
-						// with that content.
 						result.setContainsConflicts(false);
-						updateIndex(base, ours, theirs, result,
-								attributes[T_OURS]);
+						if (isSymLink) {
+							if (modeO != 0) {
+								keep(ourDce);
+							} else {
+								// Check out theirs
+								if (isWorktreeDirty(work, ourDce)) {
+									return false;
+								}
+								DirCacheEntry e = add(tw.getRawPath(), theirs,
+										DirCacheEntry.STAGE_0, EPOCH, 0);
+								if (e != null) {
+									addToCheckout(tw.getPathString(), e,
+											attributes);
+								}
+							}
+						} else {
+							// In case a conflict is detected the working tree
+							// file is again filled with new content (containing
+							// conflict markers). But also stage 0 of the index
+							// is filled with that content.
+							updateIndex(base, ours, theirs, result,
+									attributes[T_OURS]);
+						}
 					} else {
-						add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, EPOCH,
-								0);
-						add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, EPOCH,
-								0);
-						DirCacheEntry e = add(tw.getRawPath(), theirs,
-								DirCacheEntry.STAGE_3, EPOCH, 0);
+						DirCacheEntry e = addConflict(base, ours, theirs);
 
 						// OURS was deleted checkout THEIRS
 						if (modeO == 0) {
@@ -975,8 +1460,9 @@ private MergeResult<RawText> contentMerge(CanonicalTreeParser base,
 	}
 
 	private boolean isIndexDirty() {
-		if (inCore)
+		if (inCore) {
 			return false;
+		}
 
 		final int modeI = tw.getRawMode(T_INDEX);
 		final int modeO = tw.getRawMode(T_OURS);
@@ -984,37 +1470,42 @@ private boolean isIndexDirty() {
 		// Index entry has to match ours to be considered clean
 		final boolean isDirty = nonTree(modeI)
 				&& !(modeO == modeI && tw.idEqual(T_INDEX, T_OURS));
-		if (isDirty)
+		if (isDirty) {
 			failingPaths
 					.put(tw.getPathString(), MergeFailureReason.DIRTY_INDEX);
+		}
 		return isDirty;
 	}
 
 	private boolean isWorktreeDirty(WorkingTreeIterator work,
 			DirCacheEntry ourDce) throws IOException {
-		if (work == null)
+		if (work == null) {
 			return false;
+		}
 
 		final int modeF = tw.getRawMode(T_FILE);
 		final int modeO = tw.getRawMode(T_OURS);
 
 		// Worktree entry has to match ours to be considered clean
 		boolean isDirty;
-		if (ourDce != null)
+		if (ourDce != null) {
 			isDirty = work.isModified(ourDce, true, reader);
-		else {
+		} else {
 			isDirty = work.isModeDifferent(modeO);
-			if (!isDirty && nonTree(modeF))
+			if (!isDirty && nonTree(modeF)) {
 				isDirty = !tw.idEqual(T_FILE, T_OURS);
+			}
 		}
 
 		// Ignore existing empty directories
 		if (isDirty && modeF == FileMode.TYPE_TREE
-				&& modeO == FileMode.TYPE_MISSING)
+				&& modeO == FileMode.TYPE_MISSING) {
 			isDirty = false;
-		if (isDirty)
+		}
+		if (isDirty) {
 			failingPaths.put(tw.getPathString(),
 					MergeFailureReason.DIRTY_WORKTREE);
+		}
 		return isDirty;
 	}
 
@@ -1029,14 +1520,12 @@ private boolean isWorktreeDirty(WorkingTreeIterator work,
 	 * @param theirs
 	 * @param result
 	 * @param attributes
-	 * @throws FileNotFoundException
 	 * @throws IOException
 	 */
 	private void updateIndex(CanonicalTreeParser base,
 			CanonicalTreeParser ours, CanonicalTreeParser theirs,
 			MergeResult<RawText> result, Attributes attributes)
-			throws FileNotFoundException,
-			IOException {
+			throws IOException {
 		TemporaryBuffer rawMerged = null;
 		try {
 			rawMerged = doMerge(result);
@@ -1046,30 +1535,26 @@ private void updateIndex(CanonicalTreeParser base,
 				// A conflict occurred, the file will contain conflict markers
 				// the index will be populated with the three stages and the
 				// workdir (if used) contains the halfway merged content.
-				add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, EPOCH, 0);
-				add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, EPOCH, 0);
-				add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, EPOCH, 0);
+				addConflict(base, ours, theirs);
 				mergeResults.put(tw.getPathString(), result);
 				return;
 			}
 
 			// No conflict occurred, the file will contain fully merged content.
 			// The index will be populated with the new merged version.
-			DirCacheEntry dce = new DirCacheEntry(tw.getPathString());
-
+			Instant lastModified = mergedFile == null ? null
+					: nonNullRepo().getFS().lastModifiedInstant(mergedFile);
 			// Set the mode for the new content. Fall back to REGULAR_FILE if
 			// we can't merge modes of OURS and THEIRS.
 			int newMode = mergeFileModes(tw.getRawMode(0), tw.getRawMode(1),
 					tw.getRawMode(2));
-			dce.setFileMode(newMode == FileMode.MISSING.getBits()
-					? FileMode.REGULAR_FILE : FileMode.fromBits(newMode));
-			if (mergedFile != null) {
-				dce.setLastModified(
-						nonNullRepo().getFS().lastModifiedInstant(mergedFile));
-				dce.setLength((int) mergedFile.length());
-			}
-			dce.setObjectId(insertMergeResult(rawMerged, attributes));
-			builder.add(dce);
+			FileMode mode = newMode == FileMode.MISSING.getBits()
+					? FileMode.REGULAR_FILE : FileMode.fromBits(newMode);
+			workTreeUpdater.insertToIndex(rawMerged.openInputStream(),
+					tw.getPathString().getBytes(UTF_8), mode,
+					DirCacheEntry.STAGE_0, lastModified,
+					(int) rawMerged.length(),
+					attributes.get(Constants.ATTR_MERGE));
 		} finally {
 			if (rawMerged != null) {
 				rawMerged.destroy();
@@ -1085,34 +1570,28 @@ private void updateIndex(CanonicalTreeParser base,
 	 * @param attributes
 	 *            the files .gitattributes entries
 	 * @return the working tree file to which the merged content was written.
-	 * @throws FileNotFoundException
 	 * @throws IOException
 	 */
 	private File writeMergedFile(TemporaryBuffer rawMerged,
 			Attributes attributes)
-			throws FileNotFoundException, IOException {
+			throws IOException {
 		File workTree = nonNullRepo().getWorkTree();
 		FS fs = nonNullRepo().getFS();
 		File of = new File(workTree, tw.getPathString());
 		File parentFolder = of.getParentFile();
+		EolStreamType eol = workTreeUpdater.detectCheckoutStreamType(attributes);
 		if (!fs.exists(parentFolder)) {
 			parentFolder.mkdirs();
 		}
-		EolStreamType streamType = EolStreamTypeUtil.detectStreamType(
-				OperationType.CHECKOUT_OP, workingTreeOptions,
-				attributes);
-		try (OutputStream os = EolStreamTypeUtil.wrapOutputStream(
-				new BufferedOutputStream(new FileOutputStream(of)),
-				streamType)) {
-			rawMerged.writeTo(os, null);
-		}
+		workTreeUpdater.updateFileWithContent(rawMerged::openInputStream,
+				eol, tw.getSmudgeCommand(attributes), of.getPath(), of);
 		return of;
 	}
 
 	private TemporaryBuffer doMerge(MergeResult<RawText> result)
 			throws IOException {
 		TemporaryBuffer.LocalFile buf = new TemporaryBuffer.LocalFile(
-				db != null ? nonNullRepo().getDirectory() : null, inCoreLimit);
+				db != null ? nonNullRepo().getDirectory() : null, workTreeUpdater.getInCoreFileSizeLimit());
 		boolean success = false;
 		try {
 			new MergeFormatter().formatMerge(buf, result,
@@ -1127,16 +1606,6 @@ private TemporaryBuffer doMerge(MergeResult<RawText> result)
 		return buf;
 	}
 
-	private ObjectId insertMergeResult(TemporaryBuffer buf,
-			Attributes attributes) throws IOException {
-		InputStream in = buf.openInputStream();
-		try (LfsInputStream is = LfsFactory.getInstance().applyCleanFilter(
-				getRepository(), in,
-				buf.length(), attributes.get(Constants.ATTR_MERGE))) {
-			return getObjectInserter().insert(OBJ_BLOB, is.getLength(), is);
-		}
-	}
-
 	/**
 	 * Try to merge filemodes. If only ours or theirs have changed the mode
 	 * (compared to base) we choose that one. If ours and theirs have equal
@@ -1154,22 +1623,26 @@ private ObjectId insertMergeResult(TemporaryBuffer buf,
 	 *         conflict
 	 */
 	private int mergeFileModes(int modeB, int modeO, int modeT) {
-		if (modeO == modeT)
+		if (modeO == modeT) {
 			return modeO;
-		if (modeB == modeO)
+		}
+		if (modeB == modeO) {
 			// Base equal to Ours -> chooses Theirs if that is not missing
 			return (modeT == FileMode.MISSING.getBits()) ? modeO : modeT;
-		if (modeB == modeT)
+		}
+		if (modeB == modeT) {
 			// Base equal to Theirs -> chooses Ours if that is not missing
 			return (modeO == FileMode.MISSING.getBits()) ? modeT : modeO;
+		}
 		return FileMode.MISSING.getBits();
 	}
 
 	private RawText getRawText(ObjectId id,
 			Attributes attributes)
 			throws IOException, BinaryBlobException {
-		if (id.equals(ObjectId.zeroId()))
-			return new RawText(new byte[] {});
+		if (id.equals(ObjectId.zeroId())) {
+			return new RawText(new byte[]{});
+		}
 
 		ObjectLoader loader = LfsFactory.getInstance().applySmudgeFilter(
 				getRepository(), reader.open(id, OBJ_BLOB),
@@ -1233,7 +1706,7 @@ public List<String> getUnmergedPaths() {
 	 *         superset of the files listed by {@link #getUnmergedPaths()}.
 	 */
 	public List<String> getModifiedFiles() {
-		return modifiedFiles;
+		return workTreeUpdater != null ? workTreeUpdater.getModifiedFiles() : modifiedFiles;
 	}
 
 	/**
@@ -1247,7 +1720,7 @@ public List<String> getModifiedFiles() {
 	 *         for this path.
 	 */
 	public Map<String, DirCacheEntry> getToBeCheckedOut() {
-		return toBeCheckedOut;
+		return workTreeUpdater.getToBeCheckedOut();
 	}
 
 	/**
@@ -1297,7 +1770,6 @@ public boolean failed() {
 	 */
 	public void setDirCache(DirCache dc) {
 		this.dircache = dc;
-		implicitDirCache = false;
 	}
 
 	/**
@@ -1352,53 +1824,48 @@ public void setWorkingTreeIterator(WorkingTreeIterator workingTreeIterator) {
 	protected boolean mergeTrees(AbstractTreeIterator baseTree,
 			RevTree headTree, RevTree mergeTree, boolean ignoreConflicts)
 			throws IOException {
+		try {
+			workTreeUpdater = inCore ?
+					WorkTreeUpdater.createInCoreWorkTreeUpdater(db, dircache, getObjectInserter()) :
+					WorkTreeUpdater.createWorkTreeUpdater(db, dircache);
+			dircache = workTreeUpdater.getLockedDirCache();
+			tw = new NameConflictTreeWalk(db, reader);
 
-		builder = dircache.builder();
-		DirCacheBuildIterator buildIt = new DirCacheBuildIterator(builder);
-
-		tw = new NameConflictTreeWalk(db, reader);
-		tw.addTree(baseTree);
-		tw.setHead(tw.addTree(headTree));
-		tw.addTree(mergeTree);
-		int dciPos = tw.addTree(buildIt);
-		if (workingTreeIterator != null) {
-			tw.addTree(workingTreeIterator);
-			workingTreeIterator.setDirCacheIterator(tw, dciPos);
-		} else {
-			tw.setFilter(TreeFilter.ANY_DIFF);
-		}
-
-		if (!mergeTreeWalk(tw, ignoreConflicts)) {
-			return false;
-		}
-
-		if (!inCore) {
-			// No problem found. The only thing left to be done is to
-			// checkout all files from "theirs" which have been selected to
-			// go into the new index.
-			checkout();
-
-			// All content-merges are successfully done. If we can now write the
-			// new index we are on quite safe ground. Even if the checkout of
-			// files coming from "theirs" fails the user can work around such
-			// failures by checking out the index again.
-			if (!builder.commit()) {
-				cleanUp();
-				throw new IndexWriteException();
+			tw.addTree(baseTree);
+			tw.setHead(tw.addTree(headTree));
+			tw.addTree(mergeTree);
+			DirCacheBuildIterator buildIt = workTreeUpdater.createDirCacheBuildIterator();
+			int dciPos = tw.addTree(buildIt);
+			if (workingTreeIterator != null) {
+				tw.addTree(workingTreeIterator);
+				workingTreeIterator.setDirCacheIterator(tw, dciPos);
+			} else {
+				tw.setFilter(TreeFilter.ANY_DIFF);
 			}
-			builder = null;
 
-		} else {
-			builder.finish();
-			builder = null;
-		}
+			if (!mergeTreeWalk(tw, ignoreConflicts)) {
+				return false;
+			}
 
-		if (getUnmergedPaths().isEmpty() && !failed()) {
-			resultTree = dircache.writeTree(getObjectInserter());
-			return true;
+			workTreeUpdater.writeWorkTreeChanges(true);
+			if (getUnmergedPaths().isEmpty() && !failed()) {
+				WorkTreeUpdater.Result result = workTreeUpdater.writeIndexChanges();
+				resultTree = result.getTreeId();
+				modifiedFiles = result.getModifiedFiles();
+				for (String f : result.getFailedToDelete()) {
+					failingPaths.put(f, MergeFailureReason.COULD_NOT_DELETE);
+				}
+				return result.getFailedToDelete().isEmpty();
+			}
+			resultTree = null;
+			return false;
+		} finally {
+			if(modifiedFiles.isEmpty()) {
+				modifiedFiles = workTreeUpdater.getModifiedFiles();
+			}
+			workTreeUpdater.close();
+			workTreeUpdater = null;
 		}
-		resultTree = null;
-		return false;
 	}
 
 	/**
@@ -1419,8 +1886,8 @@ protected boolean mergeTreeWalk(TreeWalk treeWalk, boolean ignoreConflicts)
 		boolean hasAttributeNodeProvider = treeWalk
 				.getAttributesNodeProvider() != null;
 		while (treeWalk.next()) {
-			Attributes[] attributes = { NO_ATTRIBUTES, NO_ATTRIBUTES,
-					NO_ATTRIBUTES };
+			Attributes[] attributes = {NO_ATTRIBUTES, NO_ATTRIBUTES,
+					NO_ATTRIBUTES};
 			if (hasAttributeNodeProvider) {
 				attributes[T_BASE] = treeWalk.getAttributes(T_BASE);
 				attributes[T_OURS] = treeWalk.getAttributes(T_OURS);
@@ -1434,11 +1901,12 @@ protected boolean mergeTreeWalk(TreeWalk treeWalk, boolean ignoreConflicts)
 					hasWorkingTreeIterator ? treeWalk.getTree(T_FILE,
 							WorkingTreeIterator.class) : null,
 					ignoreConflicts, attributes)) {
-				cleanUp();
+				workTreeUpdater.revertModifiedFiles();
 				return false;
 			}
-			if (treeWalk.isSubtree() && enterSubtree)
+			if (treeWalk.isSubtree() && enterSubtree) {
 				treeWalk.enterSubtree();
+			}
 		}
 		return true;
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/AbstractRevQueue.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/AbstractRevQueue.java
index e0c7bdd..dda108b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/AbstractRevQueue.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/AbstractRevQueue.java
@@ -66,7 +66,7 @@ public final void add(RevCommit c, RevFlag queueControl) {
 	 *            flag that controls admission to the queue.
 	 */
 	public final void addParents(RevCommit c, RevFlag queueControl) {
-		final RevCommit[] pList = c.parents;
+		final RevCommit[] pList = c.getParents();
 		if (pList == null) {
 			return;
 		}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BoundaryGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BoundaryGenerator.java
index 8b78d06..bdf63ff 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BoundaryGenerator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BoundaryGenerator.java
@@ -76,11 +76,12 @@ RevCommit next() throws MissingObjectException,
 				IncorrectObjectTypeException, IOException {
 			RevCommit c = source.next();
 			if (c != null) {
-				for (int i = 0; i < c.parents.length; i++) {
+				int n = c.getParentCount();
+				for (int i = 0; i < n; i++) {
 					if (firstParent && i > 0) {
 						break;
 					}
-					RevCommit p = c.parents[i];
+					RevCommit p = c.getParent(i);
 					if ((p.flags & UNINTERESTING) != 0) {
 						held.add(p);
 					}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthGenerator.java
index 3a2cb8b..ec0824c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthGenerator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthGenerator.java
@@ -164,11 +164,12 @@ RevCommit next() throws MissingObjectException,
 
 			int newDepth = c.depth + 1;
 
-			for (int i = 0; i < c.parents.length; i++) {
+			int n = c.getParentCount();
+			for (int i = 0; i < n; i++) {
 				if (firstParent && i > 0) {
 					break;
 				}
-				RevCommit p = c.parents[i];
+				RevCommit p = c.getParent(i);
 				DepthWalk.Commit dp = (DepthWalk.Commit) p;
 
 				// If no depth has been assigned to this commit, assign
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/MergeBaseGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/MergeBaseGenerator.java
index c0fea75..a213dd4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/MergeBaseGenerator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/MergeBaseGenerator.java
@@ -114,7 +114,7 @@ private RevCommit _next() throws MissingObjectException,
 				return null;
 			}
 
-			for (RevCommit p : c.parents) {
+			for (RevCommit p : c.getParents()) {
 				if ((p.flags & IN_PENDING) != 0)
 					continue;
 				if ((p.flags & PARSED) == 0)
@@ -180,7 +180,7 @@ private void carryOntoHistory(RevCommit c, int carry) {
 
 	private void carryOntoHistoryInnerLoop(RevCommit c, int carry) {
 		for (;;) {
-			RevCommit[] parents = c.parents;
+			RevCommit[] parents = c.getParents();
 			if (parents == null || parents.length == 0) {
 				break;
 			}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PendingGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PendingGenerator.java
index add387d..a49f787 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PendingGenerator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PendingGenerator.java
@@ -108,8 +108,9 @@ RevCommit next() throws MissingObjectException,
 					produce = filter.include(walker, c);
 				}
 
-				for (int i = 0; i < c.parents.length; i++) {
-					RevCommit p = c.parents[i];
+				int parentCount = c.getParentCount();
+				for (int i = 0; i < parentCount; i++) {
+					RevCommit p = c.getParent(i);
 					// If the commit is uninteresting, don't try to prune
 					// parents because we want the maximal uninteresting set.
 					if (firstParent && i > 0 && (c.flags & UNINTERESTING) == 0) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommit.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommit.java
index 82725f3..6b644ce 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommit.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommit.java
@@ -36,6 +36,11 @@
 
 /**
  * A commit reference to a commit in the DAG.
+ *
+ * The state of the RevCommit isn't populated until the commit is parsed. The
+ * newly created RevCommit is unparsed and only has an objectId reference. Other
+ * states like parents, trees, commit ident, commit message, etc. are
+ * populated/available when the commit is parsed.
  */
 public class RevCommit extends RevObject {
 	private static final int STACK_DEPTH = 500;
@@ -102,7 +107,14 @@ public static RevCommit parse(RevWalk rw, byte[] raw) throws IOException {
 
 	private RevTree tree;
 
-	RevCommit[] parents;
+	/**
+	 * Avoid accessing this field directly. Use method
+	 * {@link RevCommit#getParents()} instead. RevCommit does not allow parents
+	 * to be overridden and altering parent(s) is not supported.
+	 *
+	 * @since 6.3
+	 */
+	protected RevCommit[] parents;
 
 	int commitTime; // An int here for performance, overflows in 2038
 
@@ -146,7 +158,7 @@ void parseCanonical(RevWalk walk, byte[] raw) throws IOException {
 		tree = walk.lookupTree(idBuffer);
 
 		int ptr = 46;
-		if (parents == null) {
+		if (getParents() == null) {
 			RevCommit[] pList = new RevCommit[1];
 			int nParents = 0;
 			for (;;) {
@@ -210,8 +222,8 @@ static void carryFlags(RevCommit c, int carry) {
 	}
 
 	private static FIFORevQueue carryFlags1(RevCommit c, int carry, int depth) {
-		for(;;) {
-			RevCommit[] pList = c.parents;
+		for (;;) {
+			RevCommit[] pList = c.getParents();
 			if (pList == null || pList.length == 0)
 				return null;
 			if (pList.length != 1) {
@@ -259,7 +271,7 @@ private static void slowCarryFlags(FIFORevQueue q, int carry) {
 		// Commits in q have non-null parent arrays and have set all
 		// flags in carry. This loop finishes copying over the graph.
 		for (RevCommit c; (c = q.next()) != null;) {
-			for (RevCommit p : c.parents)
+			for (RevCommit p : c.getParents())
 				carryOneStep(q, carry, p);
 		}
 	}
@@ -267,7 +279,7 @@ private static void slowCarryFlags(FIFORevQueue q, int carry) {
 	private static void carryOneStep(FIFORevQueue q, int carry, RevCommit c) {
 		if ((c.flags & carry) != carry) {
 			c.flags |= carry;
-			if (c.parents != null)
+			if (c.getParents() != null)
 				q.add(c);
 		}
 	}
@@ -313,8 +325,8 @@ public final RevTree getTree() {
 	 *
 	 * @return number of parents; always a positive value but can be 0.
 	 */
-	public final int getParentCount() {
-		return parents.length;
+	public int getParentCount() {
+		return parents == null ? 0 : parents.length;
 	}
 
 	/**
@@ -327,7 +339,7 @@ public final int getParentCount() {
 	 * @throws java.lang.ArrayIndexOutOfBoundsException
 	 *             an invalid parent index was specified.
 	 */
-	public final RevCommit getParent(int nth) {
+	public RevCommit getParent(int nth) {
 		return parents[nth];
 	}
 
@@ -341,7 +353,7 @@ public final RevCommit getParent(int nth) {
 	 *
 	 * @return the array of parents.
 	 */
-	public final RevCommit[] getParents() {
+	public RevCommit[] getParents() {
 		return parents;
 	}
 
@@ -353,9 +365,9 @@ public final RevCommit getParent(int nth) {
 	 * this buffer should be very careful to ensure they do not modify its
 	 * contents during their use of it.
 	 *
-	 * @return the raw unparsed commit body. This is <b>NOT A COPY</b>.
-	 *         Altering the contents of this buffer may alter the walker's
-	 *         knowledge of this commit, and the results it produces.
+	 * @return the raw unparsed commit body. This is <b>NOT A COPY</b>. Altering
+	 *         the contents of this buffer may alter the walker's knowledge of
+	 *         this commit, and the results it produces.
 	 */
 	public final byte[] getRawBuffer() {
 		return buffer;
@@ -380,7 +392,7 @@ public final RevCommit getParent(int nth) {
 	 */
 	public final byte[] getRawGpgSignature() {
 		final byte[] raw = buffer;
-		final byte[] header = {'g', 'p', 'g', 's', 'i', 'g'};
+		final byte[] header = { 'g', 'p', 'g', 's', 'i', 'g' };
 		final int start = RawParseUtils.headerStart(header, raw, 0);
 		if (start < 0) {
 			return null;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
index a25948e..8da36c5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
@@ -154,7 +154,9 @@ public class RevWalk implements Iterable<RevCommit>, AutoCloseable {
 	 */
 	static final int TREE_REV_FILTER_APPLIED = 1 << 7;
 
-	/** Number of flag bits we keep internal for our own use. See above flags. */
+	/**
+	 * Number of flag bits we keep internal for our own use. See above flags.
+	 */
 	static final int RESERVED_FLAGS = 8;
 
 	private static final int APP_FLAGS = -1 & ~((1 << RESERVED_FLAGS) - 1);
@@ -196,9 +198,7 @@ public class RevWalk implements Iterable<RevCommit>, AutoCloseable {
 	boolean shallowCommitsInitialized;
 
 	private enum GetMergedIntoStrategy {
-		RETURN_ON_FIRST_FOUND,
-		RETURN_ON_FIRST_NOT_FOUND,
-		EVALUATE_ALL
+		RETURN_ON_FIRST_FOUND, RETURN_ON_FIRST_NOT_FOUND, EVALUATE_ALL
 	}
 
 	/**
@@ -219,8 +219,8 @@ public RevWalk(Repository repo) {
 	 *
 	 * @param or
 	 *            the reader the walker will obtain data from. The reader is not
-	 *            closed when the walker is closed (but is closed by {@link
-	 *            #dispose()}.
+	 *            closed when the walker is closed (but is closed by
+	 *            {@link #dispose()}.
 	 */
 	public RevWalk(ObjectReader or) {
 		this(or, false);
@@ -381,9 +381,8 @@ public void markStart(Collection<RevCommit> list)
 	 * @throws java.io.IOException
 	 *             a pack file or loose object could not be read.
 	 */
-	public void markUninteresting(RevCommit c)
-			throws MissingObjectException, IncorrectObjectTypeException,
-			IOException {
+	public void markUninteresting(RevCommit c) throws MissingObjectException,
+			IncorrectObjectTypeException, IOException {
 		c.flags |= UNINTERESTING;
 		carryFlagsImpl(c);
 		markStart(c);
@@ -392,8 +391,8 @@ public void markUninteresting(RevCommit c)
 	/**
 	 * Determine if a commit is reachable from another commit.
 	 * <p>
-	 * A commit <code>base</code> is an ancestor of <code>tip</code> if we
-	 * can find a path of commits that leads from <code>tip</code> and ends at
+	 * A commit <code>base</code> is an ancestor of <code>tip</code> if we can
+	 * find a path of commits that leads from <code>tip</code> and ends at
 	 * <code>base</code>.
 	 * <p>
 	 * This utility function resets the walker, inserts the two supplied
@@ -462,7 +461,7 @@ public boolean isMergedInto(RevCommit base, RevCommit tip)
 	 * @since 5.12
 	 */
 	public List<Ref> getMergedInto(RevCommit commit, Collection<Ref> refs)
-			throws IOException{
+			throws IOException {
 		return getMergedInto(commit, refs, NullProgressMonitor.INSTANCE);
 	}
 
@@ -486,9 +485,8 @@ public List<Ref> getMergedInto(RevCommit commit, Collection<Ref> refs)
 	 * @since 5.12
 	 */
 	public List<Ref> getMergedInto(RevCommit commit, Collection<Ref> refs,
-					ProgressMonitor monitor) throws IOException{
-		return getMergedInto(commit, refs,
-				GetMergedIntoStrategy.EVALUATE_ALL,
+			ProgressMonitor monitor) throws IOException {
+		return getMergedInto(commit, refs, GetMergedIntoStrategy.EVALUATE_ALL,
 				monitor);
 	}
 
@@ -531,12 +529,11 @@ public boolean isMergedIntoAll(RevCommit commit, Collection<Ref> refs)
 			throws IOException {
 		return getMergedInto(commit, refs,
 				GetMergedIntoStrategy.RETURN_ON_FIRST_NOT_FOUND,
-				NullProgressMonitor.INSTANCE).size()
-				== refs.size();
+				NullProgressMonitor.INSTANCE).size() == refs.size();
 	}
 
 	private List<Ref> getMergedInto(RevCommit needle, Collection<Ref> haystacks,
-				Enum returnStrategy, ProgressMonitor monitor) throws IOException {
+			Enum returnStrategy, ProgressMonitor monitor) throws IOException {
 		List<Ref> result = new ArrayList<>();
 		List<RevCommit> uninteresting = new ArrayList<>();
 		List<RevCommit> marked = new ArrayList<>();
@@ -547,7 +544,7 @@ private List<Ref> getMergedInto(RevCommit needle, Collection<Ref> haystacks,
 			reset(~freeFlags & APP_FLAGS);
 			filter = RevFilter.ALL;
 			treeFilter = TreeFilter.ALL;
-			for (Ref r: haystacks) {
+			for (Ref r : haystacks) {
 				if (monitor.isCancelled()) {
 					return result;
 				}
@@ -574,7 +571,7 @@ private List<Ref> getMergedInto(RevCommit needle, Collection<Ref> haystacks,
 						break;
 					}
 				}
-				if(!commitFound){
+				if (!commitFound) {
 					markUninteresting(c);
 					uninteresting.add(c);
 					if (returnStrategy == GetMergedIntoStrategy.RETURN_ON_FIRST_NOT_FOUND) {
@@ -990,9 +987,8 @@ public RevObject lookupOrNull(AnyObjectId id) {
 	 *             a pack file or loose object could not be read.
 	 */
 	@NonNull
-	public RevCommit parseCommit(AnyObjectId id)
-			throws MissingObjectException, IncorrectObjectTypeException,
-			IOException {
+	public RevCommit parseCommit(AnyObjectId id) throws MissingObjectException,
+			IncorrectObjectTypeException, IOException {
 		RevObject c = peel(parseAny(id));
 		if (!(c instanceof RevCommit))
 			throw new IncorrectObjectTypeException(id.toObjectId(),
@@ -1018,9 +1014,8 @@ public RevCommit parseCommit(AnyObjectId id)
 	 *             a pack file or loose object could not be read.
 	 */
 	@NonNull
-	public RevTree parseTree(AnyObjectId id)
-			throws MissingObjectException, IncorrectObjectTypeException,
-			IOException {
+	public RevTree parseTree(AnyObjectId id) throws MissingObjectException,
+			IncorrectObjectTypeException, IOException {
 		RevObject c = peel(parseAny(id));
 
 		final RevTree t;
@@ -1274,8 +1269,8 @@ public void parseBody(RevObject obj)
 	 * @throws java.io.IOException
 	 *             a pack file or loose object could not be read.
 	 */
-	public RevObject peel(RevObject obj) throws MissingObjectException,
-			IOException {
+	public RevObject peel(RevObject obj)
+			throws MissingObjectException, IOException {
 		while (obj instanceof RevTag) {
 			parseHeaders(obj);
 			obj = ((RevTag) obj).getObject();
@@ -1304,9 +1299,9 @@ public RevFlag newFlag(String name) {
 
 	int allocFlag() {
 		if (freeFlags == 0)
-			throw new IllegalArgumentException(MessageFormat.format(
-					JGitText.get().flagsAlreadyCreated,
-					Integer.valueOf(32 - RESERVED_FLAGS)));
+			throw new IllegalArgumentException(
+					MessageFormat.format(JGitText.get().flagsAlreadyCreated,
+							Integer.valueOf(32 - RESERVED_FLAGS)));
 		final int m = Integer.lowestOneBit(freeFlags);
 		freeFlags &= ~m;
 		return m;
@@ -1323,9 +1318,11 @@ int allocFlag() {
 	 */
 	public void carry(RevFlag flag) {
 		if ((freeFlags & flag.mask) != 0)
-			throw new IllegalArgumentException(MessageFormat.format(JGitText.get().flagIsDisposed, flag.name));
+			throw new IllegalArgumentException(MessageFormat
+					.format(JGitText.get().flagIsDisposed, flag.name));
 		if (flag.walker != this)
-			throw new IllegalArgumentException(MessageFormat.format(JGitText.get().flagNotFromThis, flag.name));
+			throw new IllegalArgumentException(MessageFormat
+					.format(JGitText.get().flagNotFromThis, flag.name));
 		carryFlags |= flag.mask;
 	}
 
@@ -1359,9 +1356,11 @@ public void carry(Collection<RevFlag> set) {
 	 */
 	public final void retainOnReset(RevFlag flag) {
 		if ((freeFlags & flag.mask) != 0)
-			throw new IllegalArgumentException(MessageFormat.format(JGitText.get().flagIsDisposed, flag.name));
+			throw new IllegalArgumentException(MessageFormat
+					.format(JGitText.get().flagIsDisposed, flag.name));
 		if (flag.walker != this)
-			throw new IllegalArgumentException(MessageFormat.format(JGitText.get().flagNotFromThis, flag.name));
+			throw new IllegalArgumentException(MessageFormat
+					.format(JGitText.get().flagNotFromThis, flag.name));
 		retainOnReset |= flag.mask;
 	}
 
@@ -1496,9 +1495,9 @@ protected void reset(int retainFlags) {
 			final RevCommit c = q.next();
 			if (c == null)
 				break;
-			if (c.parents == null)
+			if (c.getParents() == null)
 				continue;
-			for (RevCommit p : c.parents) {
+			for (RevCommit p : c.getParents()) {
 				if ((p.flags & clearFlags) == 0)
 					continue;
 				p.flags &= retainFlags;
@@ -1538,7 +1537,8 @@ public void dispose() {
 	 * Like {@link #next()}, but if a checked exception is thrown during the
 	 * walk it is rethrown as a {@link RevWalkException}.
 	 *
-	 * @throws RevWalkException if an {@link IOException} was thrown.
+	 * @throws RevWalkException
+	 *             if an {@link IOException} was thrown.
 	 * @return next most recent commit; null if traversal is over.
 	 */
 	@Nullable
@@ -1598,7 +1598,8 @@ public void remove() {
 	protected void assertNotStarted() {
 		if (isNotStarted())
 			return;
-		throw new IllegalStateException(JGitText.get().outputHasAlreadyBeenStarted);
+		throw new IllegalStateException(
+				JGitText.get().outputHasAlreadyBeenStarted);
 	}
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RewriteGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RewriteGenerator.java
index 1adef07..2c88bb8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RewriteGenerator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RewriteGenerator.java
@@ -72,7 +72,7 @@ RevCommit next() throws MissingObjectException,
 		applyFilterToParents(c);
 
 		boolean rewrote = false;
-		final RevCommit[] pList = c.parents;
+		final RevCommit[] pList = c.getParents();
 		final int nParents = pList.length;
 		for (int i = 0; i < nParents; i++) {
 			final RevCommit oldp = pList[i];
@@ -108,7 +108,7 @@ RevCommit next() throws MissingObjectException,
 	private void applyFilterToParents(RevCommit c)
 			throws MissingObjectException, IncorrectObjectTypeException,
 			IOException {
-		for (RevCommit parent : c.parents) {
+		for (RevCommit parent : c.getParents()) {
 			while ((parent.flags & RevWalk.TREE_REV_FILTER_APPLIED) == 0) {
 
 				RevCommit n = source.next();
@@ -130,7 +130,7 @@ private RevCommit rewrite(RevCommit p) throws MissingObjectException,
 			IncorrectObjectTypeException, IOException {
 		for (;;) {
 
-			if (p.parents.length > 1) {
+			if (p.getParentCount() > 1) {
 				// This parent is a merge, so keep it.
 				//
 				return p;
@@ -150,15 +150,15 @@ private RevCommit rewrite(RevCommit p) throws MissingObjectException,
 				return p;
 			}
 
-			if (p.parents.length == 0) {
+			if (p.getParentCount() == 0) {
 				// We can't go back any further, other than to
 				// just delete the parent entirely.
 				//
 				return null;
 			}
 
-			applyFilterToParents(p.parents[0]);
-			p = p.parents[0];
+			applyFilterToParents(p.getParent(0));
+			p = p.getParent(0);
 
 		}
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/TopoNonIntermixSortGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/TopoNonIntermixSortGenerator.java
index 4f6d417..452545a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/TopoNonIntermixSortGenerator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/TopoNonIntermixSortGenerator.java
@@ -48,7 +48,7 @@ class TopoNonIntermixSortGenerator extends Generator {
 				break;
 			}
 			if ((c.flags & TOPO_QUEUED) == 0) {
-				for (RevCommit p : c.parents) {
+				for (RevCommit p : c.getParents()) {
 					p.inDegree++;
 
 					if (firstParent) {
@@ -94,7 +94,7 @@ RevCommit next() throws MissingObjectException,
 				continue;
 			}
 
-			for (RevCommit p : c.parents) {
+			for (RevCommit p : c.getParents()) {
 				if (--p.inDegree == 0 && (p.flags & TOPO_QUEUED) != 0) {
 					// The parent has no unproduced interesting children. unpop
 					// the parent so it goes right behind this child. This means
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/TopoSortGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/TopoSortGenerator.java
index 7a5db43..4739f78 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/TopoSortGenerator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/TopoSortGenerator.java
@@ -47,7 +47,7 @@ class TopoSortGenerator extends Generator {
 			if (c == null) {
 				break;
 			}
-			for (RevCommit p : c.parents) {
+			for (RevCommit p : c.getParents()) {
 				p.inDegree++;
 				if (firstParent) {
 					break;
@@ -86,7 +86,7 @@ RevCommit next() throws MissingObjectException,
 			// All of our children have already produced,
 			// so it is OK for us to produce now as well.
 			//
-			for (RevCommit p : c.parents) {
+			for (RevCommit p : c.getParents()) {
 				if (--p.inDegree == 0 && (p.flags & TOPO_DELAY) != 0) {
 					// This parent tried to come before us, but we are
 					// his last child. unpop the parent so it goes right
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/TreeRevFilter.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/TreeRevFilter.java
index 92d7226..f921449 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/TreeRevFilter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/TreeRevFilter.java
@@ -44,6 +44,7 @@ public class TreeRevFilter extends RevFilter {
 	private static final int FILTER_APPLIED = RevWalk.TREE_REV_FILTER_APPLIED;
 
 	private final int rewriteFlag;
+
 	private final TreeWalk pathFilter;
 
 	/**
@@ -62,10 +63,9 @@ public TreeRevFilter(RevWalk walker, TreeFilter t) {
 		this(walker, t, 0);
 	}
 
-
 	/**
-	 * Create a filter for the first phase of a parent-rewriting limited revision
-	 * walk.
+	 * Create a filter for the first phase of a parent-rewriting limited
+	 * revision walk.
 	 * <p>
 	 * This filter is ANDed to evaluate before all other filters and ties the
 	 * configured {@link TreeFilter} into the revision walking process.
@@ -79,8 +79,8 @@ public TreeRevFilter(RevWalk walker, TreeFilter t) {
 	 * @param walker
 	 *            walker used for reading trees.
 	 * @param t
-	 *            filter to compare against any changed paths in each commit. If a
-	 *            {@link FollowFilter}, will be replaced with a new filter
+	 *            filter to compare against any changed paths in each commit. If
+	 *            a {@link FollowFilter}, will be replaced with a new filter
 	 *            following new paths after a rename.
 	 * @param rewriteFlag
 	 *            flag to color commits to be removed from the simplified DAT.
@@ -106,12 +106,12 @@ public boolean include(RevWalk walker, RevCommit c)
 		c.flags |= FILTER_APPLIED;
 		// Reset the tree filter to scan this commit and parents.
 		//
-		RevCommit[] pList = c.parents;
+		RevCommit[] pList = c.getParents();
 		int nParents = pList.length;
 		TreeWalk tw = pathFilter;
 		ObjectId[] trees = new ObjectId[nParents + 1];
 		for (int i = 0; i < nParents; i++) {
-			RevCommit p = c.parents[i];
+			RevCommit p = c.getParent(i);
 			if ((p.flags & PARSED) == 0) {
 				p.parseHeaders(walker);
 			}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java
index 3f167cc..be36d2b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java
@@ -12,16 +12,30 @@
 
 package org.eclipse.jgit.transport;
 
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_DELIM;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_DEEPEN;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_DEEPEN_NOT;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_DEEPEN_SINCE;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_DONE;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_END;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_ERR;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_HAVE;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_SHALLOW;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_UNSHALLOW;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_WANT;
+
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.text.MessageFormat;
+import java.time.Instant;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
 import java.util.HashSet;
 import java.util.LinkedHashSet;
+import java.util.List;
 import java.util.Set;
 
 import org.eclipse.jgit.errors.PackProtocolException;
@@ -32,6 +46,7 @@
 import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.MutableObjectId;
 import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ObjectDatabase;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectInserter;
 import org.eclipse.jgit.lib.ProgressMonitor;
@@ -76,7 +91,7 @@ public abstract class BasePackFetchConnection extends BasePackConnection
 	/**
 	 * Maximum number of 'have' lines to send before giving up.
 	 * <p>
-	 * During {@link #negotiate(ProgressMonitor)} we send at most this many
+	 * During {@link #negotiate(ProgressMonitor, boolean, Set)} we send at most this many
 	 * commits to the remote peer as 'have' lines without an ACK response before
 	 * we give up.
 	 */
@@ -210,6 +225,12 @@ public abstract class BasePackFetchConnection extends BasePackConnection
 
 	private int maxHaves;
 
+	private Integer depth;
+
+	private Instant deepenSince;
+
+	private List<String> deepenNots;
+
 	/**
 	 * RPC state, if {@link BasePackConnection#statelessRPC} is true or protocol
 	 * V2 is used.
@@ -246,6 +267,9 @@ public BasePackFetchConnection(PackTransport packTransport) {
 		includeTags = transport.getTagOpt() != TagOpt.NO_TAGS;
 		thinPack = transport.isFetchThin();
 		filterSpec = transport.getFilterSpec();
+		depth = transport.getDepth();
+		deepenSince = transport.getDeepenSince();
+		deepenNots = transport.getDeepenNots();
 
 		if (local != null) {
 			walk = new RevWalk(local);
@@ -385,9 +409,17 @@ protected void doFetch(final ProgressMonitor monitor,
 			}
 			PacketLineOut output = statelessRPC ? pckState : pckOut;
 			if (sendWants(want, output)) {
+				boolean mayHaveShallow = depth != null || deepenSince != null || !deepenNots.isEmpty();
+				Set<ObjectId> shallowCommits = local.getObjectDatabase().getShallowCommits();
+				if (isCapableOf(GitProtocolConstants.CAPABILITY_SHALLOW)) {
+					sendShallow(shallowCommits, output);
+				} else if (mayHaveShallow) {
+					throw new PackProtocolException(JGitText.get().shallowNotSupported);
+				}
 				output.end();
 				outNeedsEnd = false;
-				negotiate(monitor);
+
+				negotiate(monitor, mayHaveShallow, shallowCommits);
 
 				clearState();
 
@@ -424,10 +456,18 @@ private void doFetchV2(ProgressMonitor monitor, Collection<Ref> want,
 		for (String capability : getCapabilitiesV2(capabilities)) {
 			pckState.writeString(capability);
 		}
+
 		if (!sendWants(want, pckState)) {
 			// We already have everything we wanted.
 			return;
 		}
+
+		Set<ObjectId> shallowCommits = local.getObjectDatabase().getShallowCommits();
+		if (capabilities.contains(GitProtocolConstants.CAPABILITY_SHALLOW)) {
+			sendShallow(shallowCommits, pckState);
+		} else if (depth != null || deepenSince != null || !deepenNots.isEmpty()) {
+			throw new PackProtocolException(JGitText.get().shallowNotSupported);
+		}
 		// If we send something, we always close it properly ourselves.
 		outNeedsEnd = false;
 
@@ -455,10 +495,21 @@ private void doFetchV2(ProgressMonitor monitor, Collection<Ref> want,
 		clearState();
 		String line = pckIn.readString();
 		// If we sent a done, we may have an error reply here.
-		if (sentDone && line.startsWith("ERR ")) { //$NON-NLS-1$
+		if (sentDone && line.startsWith(PACKET_ERR)) {
 			throw new RemoteRepositoryException(uri, line.substring(4));
 		}
-		// "shallow-info", "wanted-refs", and "packfile-uris" would have to be
+
+		if (GitProtocolConstants.SECTION_SHALLOW_INFO.equals(line)) {
+			line = handleShallowUnshallow(shallowCommits, pckIn);
+			if (!PacketLineIn.isDelimiter(line)) {
+				throw new PackProtocolException(MessageFormat
+						.format(JGitText.get().expectedGot, PACKET_DELIM,
+								line));
+			}
+			line = pckIn.readString();
+		}
+
+		// "wanted-refs" and "packfile-uris" would have to be
 		// handled here in that order.
 		if (!GitProtocolConstants.SECTION_PACKFILE.equals(line)) {
 			throw new PackProtocolException(
@@ -494,7 +545,7 @@ private boolean sendNextHaveBatch(FetchStateV2 fetchState,
 			if (c == null) {
 				break;
 			}
-			output.writeString("have " + c.getId().name() + '\n'); //$NON-NLS-1$
+			output.writeString(PACKET_HAVE + c.getId().name() + '\n');
 			n++;
 			if (n % 10 == 0 && monitor.isCancelled()) {
 				throw new CancelledException();
@@ -505,7 +556,7 @@ private boolean sendNextHaveBatch(FetchStateV2 fetchState,
 				|| (fetchState.hadAcks
 						&& fetchState.havesWithoutAck > MAX_HAVES)
 				|| fetchState.havesTotal > maxHaves) {
-			output.writeString("done\n"); //$NON-NLS-1$
+			output.writeString(PACKET_DONE + '\n');
 			output.end();
 			return true;
 		}
@@ -572,11 +623,12 @@ private boolean readAcknowledgments(FetchStateV2 fetchState,
 		if (gotReady) {
 			if (!PacketLineIn.isDelimiter(line)) {
 				throw new PackProtocolException(MessageFormat
-						.format(JGitText.get().expectedGot, "0001", line)); //$NON-NLS-1$
+						.format(JGitText.get().expectedGot, PACKET_DELIM,
+								line));
 			}
 		} else if (!PacketLineIn.isEnd(line)) {
 			throw new PackProtocolException(MessageFormat
-					.format(JGitText.get().expectedGot, "0000", line)); //$NON-NLS-1$
+					.format(JGitText.get().expectedGot, PACKET_END, line));
 		}
 		return gotReady;
 	}
@@ -672,21 +724,23 @@ private boolean sendWants(Collection<Ref> want, PacketLineOut p)
 			if (objectId == null) {
 				continue;
 			}
-			try {
-				if (walk.parseAny(objectId).has(REACHABLE)) {
-					// We already have this object. Asking for it is
-					// not a very good idea.
-					//
-					continue;
+			// if depth is set we need to fetch the objects even if they are already available
+			if (transport.getDepth() == null) {
+				try {
+					if (walk.parseAny(objectId).has(REACHABLE)) {
+						// We already have this object. Asking for it is
+						// not a very good idea.
+						//
+						continue;
+					}
+				} catch (IOException err) {
+					// Its OK, we don't have it, but we want to fix that
+					// by fetching the object from the other side.
 				}
-			} catch (IOException err) {
-				// Its OK, we don't have it, but we want to fix that
-				// by fetching the object from the other side.
 			}
 
 			final StringBuilder line = new StringBuilder(46);
-			line.append("want "); //$NON-NLS-1$
-			line.append(objectId.name());
+			line.append(PACKET_WANT).append(objectId.name());
 			if (first && TransferConfig.ProtocolVersion.V0
 					.equals(getProtocolVersion())) {
 				line.append(enableCapabilities());
@@ -773,8 +827,8 @@ else if (wantCapability(line, OPTION_SIDE_BAND))
 		return line.toString();
 	}
 
-	private void negotiate(ProgressMonitor monitor) throws IOException,
-			CancelledException {
+	private void negotiate(ProgressMonitor monitor, boolean mayHaveShallow, Set<ObjectId> shallowCommits)
+			throws IOException, CancelledException {
 		final MutableObjectId ackId = new MutableObjectId();
 		int resultsPending = 0;
 		int havesSent = 0;
@@ -795,7 +849,7 @@ private void negotiate(ProgressMonitor monitor) throws IOException,
 			}
 
 			ObjectId o = c.getId();
-			pckOut.writeString("have " + o.name() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
+			pckOut.writeString(PACKET_HAVE + o.name() + '\n');
 			havesSent++;
 			havesSinceLastContinue++;
 
@@ -898,7 +952,7 @@ private void negotiate(ProgressMonitor monitor) throws IOException,
 			// loop above while in the middle of a request. This allows us
 			// to just write done immediately.
 			//
-			pckOut.writeString("done\n"); //$NON-NLS-1$
+			pckOut.writeString(PACKET_DONE + '\n');
 			pckOut.flush();
 		}
 
@@ -911,6 +965,14 @@ private void negotiate(ProgressMonitor monitor) throws IOException,
 			resultsPending++;
 		}
 
+		if (mayHaveShallow) {
+			String line = handleShallowUnshallow(shallowCommits, pckIn);
+			if (!PacketLineIn.isEnd(line)) {
+				throw new PackProtocolException(MessageFormat
+						.format(JGitText.get().expectedGot, PACKET_END, line));
+			}
+		}
+
 		READ_RESULT: while (resultsPending > 0 || multiAck != MultiAck.OFF) {
 			final AckNackResult anr = pckIn.readACK(ackId);
 			resultsPending--;
@@ -992,7 +1054,7 @@ private void markAdvertised(AnyObjectId id) {
 	private void markCommon(RevObject obj, AckNackResult anr, boolean useState)
 			throws IOException {
 		if (useState && anr == AckNackResult.ACK_COMMON && !obj.has(STATE)) {
-			pckState.writeString("have " + obj.name() + '\n'); //$NON-NLS-1$
+			pckState.writeString(PACKET_HAVE + obj.name() + '\n');
 			obj.add(STATE);
 		}
 		obj.add(COMMON);
@@ -1025,6 +1087,55 @@ private void receivePack(final ProgressMonitor monitor,
 		}
 	}
 
+	private void sendShallow(Set<ObjectId> shallowCommits, PacketLineOut output)
+			throws IOException {
+		for (ObjectId shallowCommit : shallowCommits) {
+			output.writeString(PACKET_SHALLOW + shallowCommit.name());
+		}
+
+		if (depth != null) {
+			output.writeString(PACKET_DEEPEN + depth);
+		}
+
+		if (deepenSince != null) {
+			output.writeString(
+					PACKET_DEEPEN_SINCE + deepenSince.getEpochSecond());
+		}
+
+		if (deepenNots != null) {
+			for (String deepenNotRef : deepenNots) {
+				output.writeString(PACKET_DEEPEN_NOT + deepenNotRef);
+			}
+		}
+	}
+
+	private String handleShallowUnshallow(
+			Set<ObjectId> advertisedShallowCommits, PacketLineIn input)
+			throws IOException {
+		String line = input.readString();
+		ObjectDatabase objectDatabase = local.getObjectDatabase();
+		HashSet<ObjectId> newShallowCommits = new HashSet<>(
+				advertisedShallowCommits);
+		while (!PacketLineIn.isDelimiter(line) && !PacketLineIn.isEnd(line)) {
+			if (line.startsWith(PACKET_SHALLOW)) {
+				newShallowCommits.add(ObjectId
+						.fromString(line.substring(PACKET_SHALLOW.length())));
+			} else if (line.startsWith(PACKET_UNSHALLOW)) {
+				ObjectId unshallow = ObjectId
+						.fromString(line.substring(PACKET_UNSHALLOW.length()));
+				if (!advertisedShallowCommits.contains(unshallow)) {
+					throw new PackProtocolException(MessageFormat.format(
+							JGitText.get().notShallowedUnshallow,
+							unshallow.name()));
+				}
+				newShallowCommits.remove(unshallow);
+			}
+			line = input.readString();
+		}
+		objectDatabase.setShallowCommits(newShallowCommits);
+		return line;
+	}
+
 	/**
 	 * Notification event delivered just before the pack is received from the
 	 * network. This event can be used by RPC such as {@link org.eclipse.jgit.transport.TransportHttp} to
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java
index bb58a7e..e0eb126 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2008, 2020 Shawn O. Pearce <spearce@spearce.org> and others
+ * Copyright (C) 2008, 2022 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
@@ -402,7 +402,7 @@ private boolean askForIsComplete() throws TransportException {
 					ow.checkConnectivity();
 				}
 			}
-			return true;
+			return transport.getDepth() == null; // if depth is set we need to request objects that are already available
 		} catch (MissingObjectException e) {
 			return false;
 		} catch (IOException e) {
@@ -525,8 +525,10 @@ private void want(Ref src, RefSpec spec)
 		}
 		if (spec.getDestination() != null) {
 			final TrackingRefUpdate tru = createUpdate(spec, newId);
-			if (newId.equals(tru.getOldObjectId()))
+			// if depth is set we need to update the ref
+			if (newId.equals(tru.getOldObjectId()) && transport.getDepth() == null) {
 				return;
+			}
 			localUpdates.add(tru);
 		}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchRequest.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchRequest.java
index 9ebc722..0663c51 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchRequest.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchRequest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018, Google LLC. and others
+ * Copyright (C) 2018, 2022 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
@@ -35,7 +35,7 @@ abstract class FetchRequest {
 
 	final int deepenSince;
 
-	final List<String> deepenNotRefs;
+	final List<String> deepenNots;
 
 	@Nullable
 	final String agent;
@@ -53,7 +53,7 @@ abstract class FetchRequest {
 	 *            the filter spec
 	 * @param clientCapabilities
 	 *            capabilities sent in the request
-	 * @param deepenNotRefs
+	 * @param deepenNots
 	 *            Requests that the shallow clone/fetch should be cut at these
 	 *            specific revisions instead of a depth.
 	 * @param deepenSince
@@ -66,14 +66,14 @@ abstract class FetchRequest {
 			@NonNull Set<ObjectId> clientShallowCommits,
 			@NonNull FilterSpec filterSpec,
 			@NonNull Set<String> clientCapabilities, int deepenSince,
-			@NonNull List<String> deepenNotRefs, @Nullable String agent) {
+			@NonNull List<String> deepenNots, @Nullable String agent) {
 		this.wantIds = requireNonNull(wantIds);
 		this.depth = depth;
 		this.clientShallowCommits = requireNonNull(clientShallowCommits);
 		this.filterSpec = requireNonNull(filterSpec);
 		this.clientCapabilities = requireNonNull(clientCapabilities);
 		this.deepenSince = deepenSince;
-		this.deepenNotRefs = requireNonNull(deepenNotRefs);
+		this.deepenNots = requireNonNull(deepenNots);
 		this.agent = agent;
 	}
 
@@ -148,8 +148,8 @@ int getDeepenSince() {
 	 * @return refs received in "deepen-not" lines.
 	 */
 	@NonNull
-	List<String> getDeepenNotRefs() {
-		return deepenNotRefs;
+	List<String> getDeepenNots() {
+		return deepenNots;
 	}
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV0Request.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV0Request.java
index 91adb5e..4decb79 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV0Request.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV0Request.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018, Google LLC. and others
+ * Copyright (C) 2018, 2022 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
@@ -11,9 +11,10 @@
 
 import static java.util.Objects.requireNonNull;
 
+import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
 
 import org.eclipse.jgit.annotations.NonNull;
@@ -28,15 +29,20 @@ final class FetchV0Request extends FetchRequest {
 	FetchV0Request(@NonNull Set<ObjectId> wantIds, int depth,
 			@NonNull Set<ObjectId> clientShallowCommits,
 			@NonNull FilterSpec filterSpec,
-			@NonNull Set<String> clientCapabilities, @Nullable String agent) {
+			@NonNull Set<String> clientCapabilities, int deepenSince,
+			@NonNull List<String> deepenNotRefs, @Nullable String agent) {
 		super(wantIds, depth, clientShallowCommits, filterSpec,
-				clientCapabilities, 0, Collections.emptyList(),	agent);
+				clientCapabilities, deepenSince, deepenNotRefs, agent);
 	}
 
 	static final class Builder {
 
 		int depth;
 
+		final List<String> deepenNots = new ArrayList<>();
+
+		int deepenSince;
+
 		final Set<ObjectId> wantIds = new HashSet<>();
 
 		final Set<ObjectId> clientShallowCommits = new HashSet<>();
@@ -68,6 +74,50 @@ Builder setDepth(int d) {
 		}
 
 		/**
+		 * @return depth set in the request (via a "deepen" line). Defaulting to
+		 *         0 if not set.
+		 */
+		int getDepth() {
+			return depth;
+		}
+
+		/**
+		 * @return true if there has been at least one "deepen not" line in the
+		 *         request so far
+		 */
+		boolean hasDeepenNots() {
+			return !deepenNots.isEmpty();
+		}
+
+		/**
+		 * @param deepenNot
+		 *            reference received in a "deepen not" line
+		 * @return this builder
+		 */
+		Builder addDeepenNot(String deepenNot) {
+			deepenNots.add(deepenNot);
+			return this;
+		}
+
+		/**
+		 * @param value
+		 *            Unix timestamp received in a "deepen since" line
+		 * @return this builder
+		 */
+		Builder setDeepenSince(int value) {
+			deepenSince = value;
+			return this;
+		}
+
+		/**
+		 * @return shallow since value, sent before in a "deepen since" line. 0
+		 *         by default.
+		 */
+		int getDeepenSince() {
+			return deepenSince;
+		}
+
+		/**
 		 * @param shallowOid
 		 *            object id received in a "shallow" line
 		 * @return this builder
@@ -110,7 +160,7 @@ Builder setFilterSpec(@NonNull FilterSpec filter) {
 
 		FetchV0Request build() {
 			return new FetchV0Request(wantIds, depth, clientShallowCommits,
-					filterSpec, clientCaps, agent);
+					filterSpec, clientCaps, deepenSince, deepenNots, agent);
 		}
 
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV2Request.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV2Request.java
index 50fb9d2..446a3bc 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV2Request.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV2Request.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018, Google LLC. and others
+ * Copyright (C) 2018, 2022 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
@@ -50,7 +50,7 @@ public final class FetchV2Request extends FetchRequest {
 			@NonNull List<String> wantedRefs,
 			@NonNull Set<ObjectId> wantIds,
 			@NonNull Set<ObjectId> clientShallowCommits, int deepenSince,
-			@NonNull List<String> deepenNotRefs, int depth,
+			@NonNull List<String> deepenNots, int depth,
 			@NonNull FilterSpec filterSpec,
 			boolean doneReceived, boolean waitForDone,
 			@NonNull Set<String> clientCapabilities,
@@ -58,7 +58,7 @@ public final class FetchV2Request extends FetchRequest {
 			boolean sidebandAll, @NonNull List<String> packfileUriProtocols) {
 		super(wantIds, depth, clientShallowCommits, filterSpec,
 				clientCapabilities, deepenSince,
-				deepenNotRefs, agent);
+				deepenNots, agent);
 		this.peerHas = requireNonNull(peerHas);
 		this.wantedRefs = requireNonNull(wantedRefs);
 		this.doneReceived = doneReceived;
@@ -140,7 +140,7 @@ static final class Builder {
 
 		final Set<ObjectId> clientShallowCommits = new HashSet<>();
 
-		final List<String> deepenNotRefs = new ArrayList<>();
+		final List<String> deepenNots = new ArrayList<>();
 
 		final Set<String> clientCapabilities = new HashSet<>();
 
@@ -240,17 +240,17 @@ int getDepth() {
 		 * @return true if there has been at least one "deepen not" line in the
 		 *         request so far
 		 */
-		boolean hasDeepenNotRefs() {
-			return !deepenNotRefs.isEmpty();
+		boolean hasDeepenNots() {
+			return !deepenNots.isEmpty();
 		}
 
 		/**
-		 * @param deepenNotRef
+		 * @param deepenNot
 		 *            reference received in a "deepen not" line
 		 * @return this builder
 		 */
-		Builder addDeepenNotRef(String deepenNotRef) {
-			deepenNotRefs.add(deepenNotRef);
+		Builder addDeepenNot(String deepenNot) {
+			deepenNots.add(deepenNot);
 			return this;
 		}
 
@@ -350,7 +350,7 @@ Builder addPackfileUriProtocol(@NonNull String value) {
 		 */
 		FetchV2Request build() {
 			return new FetchV2Request(peerHas, wantedRefs, wantIds,
-					clientShallowCommits, deepenSince, deepenNotRefs,
+					clientShallowCommits, deepenSince, deepenNots,
 					depth, filterSpec, doneReceived, waitForDone, clientCapabilities,
 					agent, Collections.unmodifiableList(serverOptions),
 					sidebandAll,
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java
index aaa9308..be14e92 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java
@@ -1,7 +1,7 @@
 /*
  * Copyright (C) 2008, 2013 Google Inc.
  * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2008, 2020 Shawn O. Pearce <spearce@spearce.org> and others
+ * Copyright (C) 2008, 2022 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
@@ -234,6 +234,13 @@ public final class GitProtocolConstants {
 	public static final String CAPABILITY_SERVER_OPTION = "server-option"; //$NON-NLS-1$
 
 	/**
+	 * The server supports the receiving of shallow options.
+	 *
+	 * @since 6.3
+	 */
+	public static final String CAPABILITY_SHALLOW = "shallow"; //$NON-NLS-1$
+
+	/**
 	 * Option for passing application-specific options to the server.
 	 *
 	 * @since 5.2
@@ -308,6 +315,13 @@ public final class GitProtocolConstants {
 	public static final String SECTION_PACKFILE = "packfile"; //$NON-NLS-1$
 
 	/**
+	 * Protocol V2 shallow-info section header.
+	 *
+	 * @since 6.3
+	 */
+	public static final String SECTION_SHALLOW_INFO = "shallow-info"; //$NON-NLS-1$
+
+	/**
 	 * Protocol announcement for protocol version 1. This is the same as V0,
 	 * except for this initial line.
 	 *
@@ -329,6 +343,106 @@ public final class GitProtocolConstants {
 	 */
 	public static final String VERSION_2_REQUEST = "version=2"; //$NON-NLS-1$
 
+	/**
+	 * The flush packet.
+	 *
+	 * @since 6.3
+	 */
+	public static final String PACKET_FLUSH = "0000"; //$NON-NLS-1$
+
+	/**
+	 * An alias for {@link #PACKET_FLUSH}. "Flush" is the name used in the C git
+	 * documentation; the Java implementation calls this "end" in several
+	 * places.
+	 *
+	 * @since 6.3
+	 */
+	public static final String PACKET_END = PACKET_FLUSH;
+
+	/**
+	 * The delimiter packet in protocol V2.
+	 *
+	 * @since 6.3
+	 */
+	public static final String PACKET_DELIM = "0001"; //$NON-NLS-1$
+
+	/**
+	 * A "deepen" packet beginning.
+	 *
+	 * @since 6.3
+	 */
+	public static final String PACKET_DEEPEN = "deepen "; //$NON-NLS-1$
+
+	/**
+	 * A "deepen-not" packet beginning.
+	 *
+	 * @since 6.3
+	 */
+	public static final String PACKET_DEEPEN_NOT = "deepen-not "; //$NON-NLS-1$
+
+	/**
+	 * A "deepen-since" packet beginning.
+	 *
+	 * @since 6.3
+	 */
+	public static final String PACKET_DEEPEN_SINCE = "deepen-since "; //$NON-NLS-1$
+
+	/**
+	 * An "ACK" packet beginning.
+	 *
+	 * @since 6.3
+	 */
+	public static final String PACKET_ACK = "ACK "; //$NON-NLS-1$
+
+	/**
+	 * A "done" packet beginning.
+	 *
+	 * @since 6.3
+	 */
+	public static final String PACKET_DONE = "done"; //$NON-NLS-1$
+
+	/**
+	 * A "ERR" packet beginning.
+	 *
+	 * @since 6.3
+	 */
+	public static final String PACKET_ERR = "ERR "; //$NON-NLS-1$
+
+	/**
+	 * A "have" packet beginning.
+	 *
+	 * @since 6.3
+	 */
+	public static final String PACKET_HAVE = "have "; //$NON-NLS-1$
+
+	/**
+	 * A "shallow" packet beginning.
+	 *
+	 * @since 6.3
+	 */
+	public static final String PACKET_SHALLOW = OPTION_SHALLOW + ' ';
+
+	/**
+	 * A "shallow" packet beginning.
+	 *
+	 * @since 6.3
+	 */
+	public static final String PACKET_UNSHALLOW = "unshallow "; //$NON-NLS-1$
+
+	/**
+	 * A "want" packet beginning.
+	 *
+	 * @since 6.3
+	 */
+	public static final String PACKET_WANT = "want "; //$NON-NLS-1$
+
+	/**
+	 * A "want-ref" packet beginning.
+	 *
+	 * @since 6.3
+	 */
+	public static final String PACKET_WANT_REF = OPTION_WANT_REF + ' ';
+
 	enum MultiAck {
 		OFF, CONTINUE, DETAILED;
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV0Parser.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV0Parser.java
index f8c51c1..21a4925 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV0Parser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV0Parser.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018, Google LLC. and others
+ * Copyright (C) 2018, 2022 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
@@ -10,6 +10,11 @@
 package org.eclipse.jgit.transport;
 
 import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_FILTER;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_DEEPEN;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_DEEPEN_NOT;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_DEEPEN_SINCE;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_SHALLOW;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_WANT;
 
 import java.io.EOFException;
 import java.io.IOException;
@@ -70,20 +75,56 @@ FetchV0Request recvWants(PacketLineIn pckIn)
 				break;
 			}
 
-			if (line.startsWith("deepen ")) { //$NON-NLS-1$
-				int depth = Integer.parseInt(line.substring(7));
+			if (line.startsWith(PACKET_DEEPEN)) {
+				int depth = Integer
+						.parseInt(line.substring(PACKET_DEEPEN.length()));
 				if (depth <= 0) {
 					throw new PackProtocolException(
 							MessageFormat.format(JGitText.get().invalidDepth,
 									Integer.valueOf(depth)));
 				}
+				if (reqBuilder.getDeepenSince() != 0) {
+					throw new PackProtocolException(
+							JGitText.get().deepenSinceWithDeepen);
+				}
+				if (reqBuilder.hasDeepenNots()) {
+					throw new PackProtocolException(
+							JGitText.get().deepenNotWithDeepen);
+				}
 				reqBuilder.setDepth(depth);
 				continue;
 			}
 
-			if (line.startsWith("shallow ")) { //$NON-NLS-1$
+			if (line.startsWith(PACKET_DEEPEN_NOT)) {
+				reqBuilder.addDeepenNot(
+						line.substring(PACKET_DEEPEN_NOT.length()));
+				if (reqBuilder.getDepth() != 0) {
+					throw new PackProtocolException(
+							JGitText.get().deepenNotWithDeepen);
+				}
+				continue;
+			}
+
+			if (line.startsWith(PACKET_DEEPEN_SINCE)) {
+				// TODO: timestamps should be long
+				int ts = Integer
+						.parseInt(line.substring(PACKET_DEEPEN_SINCE.length()));
+				if (ts <= 0) {
+					throw new PackProtocolException(MessageFormat
+							.format(JGitText.get().invalidTimestamp, line));
+				}
+				if (reqBuilder.getDepth() != 0) {
+					throw new PackProtocolException(
+							JGitText.get().deepenSinceWithDeepen);
+				}
+				reqBuilder.setDeepenSince(ts);
+				continue;
+			}
+
+			if (line.startsWith(PACKET_SHALLOW)) {
 				reqBuilder.addClientShallowCommit(
-						ObjectId.fromString(line.substring(8)));
+						ObjectId.fromString(
+								line.substring(PACKET_SHALLOW.length())));
 				continue;
 			}
 
@@ -101,7 +142,7 @@ FetchV0Request recvWants(PacketLineIn pckIn)
 				continue;
 			}
 
-			if (!line.startsWith("want ") || line.length() < 45) { //$NON-NLS-1$
+			if (!line.startsWith(PACKET_WANT) || line.length() < 45) {
 				throw new PackProtocolException(MessageFormat
 						.format(JGitText.get().expectedGot, "want", line)); //$NON-NLS-1$
 			}
@@ -115,7 +156,8 @@ FetchV0Request recvWants(PacketLineIn pckIn)
 				}
 			}
 
-			reqBuilder.addWantId(ObjectId.fromString(line.substring(5)));
+			reqBuilder.addWantId(
+					ObjectId.fromString(line.substring(PACKET_WANT.length())));
 			isFirst = false;
 		}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java
index 6cec4b9..b38deb6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018, Google LLC. and others
+ * Copyright (C) 2018, 2022 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
@@ -20,7 +20,14 @@
 import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDE_BAND_64K;
 import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_THIN_PACK;
 import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_WAIT_FOR_DONE;
-import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_WANT_REF;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_DEEPEN;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_DEEPEN_NOT;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_DEEPEN_SINCE;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_DONE;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_HAVE;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_SHALLOW;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_WANT;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_WANT_REF;
 
 import java.io.IOException;
 import java.text.MessageFormat;
@@ -115,15 +122,17 @@ FetchV2Request parseFetchRequest(PacketLineIn pckIn)
 
 		boolean filterReceived = false;
 		for (String line2 : pckIn.readStrings()) {
-			if (line2.startsWith("want ")) { //$NON-NLS-1$
-				reqBuilder.addWantId(ObjectId.fromString(line2.substring(5)));
+			if (line2.startsWith(PACKET_WANT)) {
+				reqBuilder.addWantId(ObjectId
+						.fromString(line2.substring(PACKET_WANT.length())));
 			} else if (transferConfig.isAllowRefInWant()
-					&& line2.startsWith(OPTION_WANT_REF + " ")) { //$NON-NLS-1$
+					&& line2.startsWith(PACKET_WANT_REF)) {
 				reqBuilder.addWantedRef(
-						line2.substring(OPTION_WANT_REF.length() + 1));
-			} else if (line2.startsWith("have ")) { //$NON-NLS-1$
-				reqBuilder.addPeerHas(ObjectId.fromString(line2.substring(5)));
-			} else if (line2.equals("done")) { //$NON-NLS-1$
+						line2.substring(PACKET_WANT_REF.length()));
+			} else if (line2.startsWith(PACKET_HAVE)) {
+				reqBuilder.addPeerHas(ObjectId
+						.fromString(line2.substring(PACKET_HAVE.length())));
+			} else if (line2.equals(PACKET_DONE)) {
 				reqBuilder.setDoneReceived();
 			} else if (line2.equals(OPTION_WAIT_FOR_DONE)) {
 				reqBuilder.setWaitForDone();
@@ -135,11 +144,13 @@ FetchV2Request parseFetchRequest(PacketLineIn pckIn)
 				reqBuilder.addClientCapability(OPTION_INCLUDE_TAG);
 			} else if (line2.equals(OPTION_OFS_DELTA)) {
 				reqBuilder.addClientCapability(OPTION_OFS_DELTA);
-			} else if (line2.startsWith("shallow ")) { //$NON-NLS-1$
+			} else if (line2.startsWith(PACKET_SHALLOW)) {
 				reqBuilder.addClientShallowCommit(
-						ObjectId.fromString(line2.substring(8)));
-			} else if (line2.startsWith("deepen ")) { //$NON-NLS-1$
-				int parsedDepth = Integer.parseInt(line2.substring(7));
+						ObjectId.fromString(
+								line2.substring(PACKET_SHALLOW.length())));
+			} else if (line2.startsWith(PACKET_DEEPEN)) {
+				int parsedDepth = Integer
+						.parseInt(line2.substring(PACKET_DEEPEN.length()));
 				if (parsedDepth <= 0) {
 					throw new PackProtocolException(
 							MessageFormat.format(JGitText.get().invalidDepth,
@@ -149,21 +160,23 @@ FetchV2Request parseFetchRequest(PacketLineIn pckIn)
 					throw new PackProtocolException(
 							JGitText.get().deepenSinceWithDeepen);
 				}
-				if (reqBuilder.hasDeepenNotRefs()) {
+				if (reqBuilder.hasDeepenNots()) {
 					throw new PackProtocolException(
 							JGitText.get().deepenNotWithDeepen);
 				}
 				reqBuilder.setDepth(parsedDepth);
-			} else if (line2.startsWith("deepen-not ")) { //$NON-NLS-1$
-				reqBuilder.addDeepenNotRef(line2.substring(11));
+			} else if (line2.startsWith(PACKET_DEEPEN_NOT)) {
+				reqBuilder.addDeepenNot(
+						line2.substring(PACKET_DEEPEN_NOT.length()));
 				if (reqBuilder.getDepth() != 0) {
 					throw new PackProtocolException(
 							JGitText.get().deepenNotWithDeepen);
 				}
 			} else if (line2.equals(OPTION_DEEPEN_RELATIVE)) {
 				reqBuilder.addClientCapability(OPTION_DEEPEN_RELATIVE);
-			} else if (line2.startsWith("deepen-since ")) { //$NON-NLS-1$
-				int ts = Integer.parseInt(line2.substring(13));
+			} else if (line2.startsWith(PACKET_DEEPEN_SINCE)) {
+				int ts = Integer.parseInt(
+						line2.substring(PACKET_DEEPEN_SINCE.length()));
 				if (ts <= 0) {
 					throw new PackProtocolException(MessageFormat
 							.format(JGitText.get().invalidTimestamp, line2));
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java
index 2542105..b70eedc 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java
@@ -20,6 +20,8 @@
 import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_REPORT_STATUS;
 import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_SIDE_BAND_64K;
 import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_AGENT;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_ERR;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_SHALLOW;
 import static org.eclipse.jgit.transport.SideBandOutputStream.CH_DATA;
 import static org.eclipse.jgit.transport.SideBandOutputStream.CH_ERROR;
 import static org.eclipse.jgit.transport.SideBandOutputStream.CH_PROGRESS;
@@ -1247,7 +1249,7 @@ private void unlockPack() throws IOException {
 	public void sendAdvertisedRefs(RefAdvertiser adv)
 			throws IOException, ServiceMayNotContinueException {
 		if (advertiseError != null) {
-			adv.writeOne("ERR " + advertiseError); //$NON-NLS-1$
+			adv.writeOne(PACKET_ERR + advertiseError);
 			return;
 		}
 
@@ -1255,7 +1257,7 @@ public void sendAdvertisedRefs(RefAdvertiser adv)
 			advertiseRefsHook.advertiseRefs(this);
 		} catch (ServiceMayNotContinueException fail) {
 			if (fail.getMessage() != null) {
-				adv.writeOne("ERR " + fail.getMessage()); //$NON-NLS-1$
+				adv.writeOne(PACKET_ERR + fail.getMessage());
 				fail.setOutput();
 			}
 			throw fail;
@@ -1339,8 +1341,9 @@ private void recvCommands() throws IOException {
 					break;
 				}
 
-				if (line.length() >= 48 && line.startsWith("shallow ")) { //$NON-NLS-1$
-					parseShallow(line.substring(8, 48));
+				int len = PACKET_SHALLOW.length() + 40;
+				if (line.length() >= len && line.startsWith(PACKET_SHALLOW)) {
+					parseShallow(line.substring(PACKET_SHALLOW.length(), len));
 					continue;
 				}
 
@@ -1795,9 +1798,9 @@ private void sendStatusReport(Throwable unpackError) throws IOException {
 			@Override
 			void sendString(String s) throws IOException {
 				if (reportStatus) {
-					pckOut.writeString(s + "\n"); //$NON-NLS-1$
+					pckOut.writeString(s + '\n');
 				} else if (msgOut != null) {
-					msgOut.write(Constants.encode(s + "\n")); //$NON-NLS-1$
+					msgOut.write(Constants.encode(s + '\n'));
 				}
 			}
 		};
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefSpec.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefSpec.java
index e9134a1..61d1935 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefSpec.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefSpec.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008, 2013 Shawn O. Pearce <spearce@spearce.org> and others
+ * Copyright (C) 2008, 2022 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
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java
index 3222d63..7cea998 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java
@@ -27,6 +27,7 @@
 import java.net.URISyntaxException;
 import java.net.URL;
 import java.text.MessageFormat;
+import java.time.Instant;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -784,6 +785,12 @@ static String findTrackingRefName(final String remoteName,
 
 	private PrePushHook prePush;
 
+	private Integer depth;
+
+	private Instant deepenSince;
+
+	private List<String> deepenNots = new ArrayList<>();
+
 	@Nullable
 	TransferConfig.ProtocolVersion protocol;
 
@@ -1086,6 +1093,83 @@ public final void setFilterSpec(@NonNull FilterSpec filter) {
 		filterSpec = requireNonNull(filter);
 	}
 
+
+	/**
+	 * Retrieves the depth for a shallow clone.
+	 *
+	 * @return the depth, or {@code null} if none set
+	 * @since 6.3
+	 */
+	public final Integer getDepth() {
+		return depth;
+	}
+
+	/**
+	 * Limits fetching to the specified number of commits from the tip of each
+	 * remote branch history.
+	 *
+	 * @param depth
+	 *            the depth
+	 * @since 6.3
+	 */
+	public final void setDepth(int depth) {
+		if (depth < 1) {
+			throw new IllegalArgumentException(JGitText.get().depthMustBeAt1);
+		}
+		this.depth = Integer.valueOf(depth);
+	}
+
+	/**
+	 * Limits fetching to the specified number of commits from the tip of each
+	 * remote branch history.
+	 *
+	 * @param depth
+	 *            the depth, or {@code null} to unset the depth
+	 * @since 6.3
+	 */
+	public final void setDepth(Integer depth) {
+		if (depth != null && depth.intValue() < 1) {
+			throw new IllegalArgumentException(JGitText.get().depthMustBeAt1);
+		}
+		this.depth = depth;
+	}
+
+	/**
+	 * @return the deepen-since for a shallow clone
+	 * @since 6.3
+	 */
+	public final Instant getDeepenSince() {
+		return deepenSince;
+	}
+
+	/**
+	 * Deepen or shorten the history of a shallow repository to include all reachable commits after a specified time.
+	 *
+	 * @param deepenSince the deepen-since. Must not be {@code null}
+	 * @since 6.3
+	 */
+	public final void setDeepenSince(@NonNull Instant deepenSince) {
+		this.deepenSince = deepenSince;
+	}
+
+	/**
+	 * @return the deepen-not for a shallow clone
+	 * @since 6.3
+	 */
+	public final List<String> getDeepenNots() {
+		return deepenNots;
+	}
+
+	/**
+	 * Deepen or shorten the history of a shallow repository to exclude commits reachable from a specified remote branch or tag.
+	 *
+	 * @param deepenNots the deepen-not. Must not be {@code null}
+	 * @since 6.3
+	 */
+	public final void setDeepenNots(@NonNull List<String> deepenNots) {
+		this.deepenNots = deepenNots;
+	}
+
 	/**
 	 * Apply provided remote configuration on this transport.
 	 *
@@ -1230,7 +1314,7 @@ public void setPushOptions(List<String> pushOptions) {
 	 * @param toFetch
 	 *            specification of refs to fetch locally. May be null or the
 	 *            empty collection to use the specifications from the
-	 *            RemoteConfig. May contains regular and negative 
+	 *            RemoteConfig. May contains regular and negative
 	 *            {@link RefSpec}s. Source for each regular RefSpec can't
 	 *            be null.
 	 * @return information describing the tracking refs updated.
@@ -1266,7 +1350,7 @@ public FetchResult fetch(final ProgressMonitor monitor,
 	 * @param toFetch
 	 *            specification of refs to fetch locally. May be null or the
 	 *            empty collection to use the specifications from the
-	 *            RemoteConfig. May contains regular and negative 
+	 *            RemoteConfig. May contain regular and negative
 	 *            {@link RefSpec}s. Source for each regular RefSpec can't
 	 *            be null.
 	 * @param branch
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 1617c50..65dbf12 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008, 2020 Google Inc. and others
+ * Copyright (C) 2008, 2022 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
@@ -10,6 +10,7 @@
 
 package org.eclipse.jgit.transport;
 
+import static java.util.Collections.emptyList;
 import static java.util.Collections.unmodifiableMap;
 import static java.util.Objects.requireNonNull;
 import static org.eclipse.jgit.lib.Constants.R_TAGS;
@@ -35,6 +36,12 @@
 import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDE_BAND_64K;
 import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_THIN_PACK;
 import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_WAIT_FOR_DONE;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_ACK;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_DONE;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_ERR;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_HAVE;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_SHALLOW;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_UNSHALLOW;
 import static org.eclipse.jgit.transport.GitProtocolConstants.VERSION_2_REQUEST;
 import static org.eclipse.jgit.util.RefMap.toRefMap;
 
@@ -1033,6 +1040,7 @@ private void service(PacketLineOut pckOut) throws IOException {
 		// writing a response. Buffer the response until then.
 		PackStatistics.Accumulator accumulator = new PackStatistics.Accumulator();
 		List<ObjectId> unshallowCommits = new ArrayList<>();
+		List<ObjectId> deepenNots = emptyList();
 		FetchRequest req;
 		try {
 			if (biDirectionalPipe)
@@ -1071,13 +1079,15 @@ else if (requestValidator instanceof AnyRequestValidator)
 				verifyClientShallow(req.getClientShallowCommits());
 			}
 
-			if (req.getDepth() != 0 || req.getDeepenSince() != 0) {
+			deepenNots = parseDeepenNots(req.getDeepenNots());
+			if (req.getDepth() != 0 || req.getDeepenSince() != 0 || !req.getDeepenNots().isEmpty()) {
 				computeShallowsAndUnshallows(req, shallow -> {
-					pckOut.writeString("shallow " + shallow.name() + '\n'); //$NON-NLS-1$
+					pckOut.writeString(PACKET_SHALLOW + shallow.name() + '\n');
 				}, unshallow -> {
-					pckOut.writeString("unshallow " + unshallow.name() + '\n'); //$NON-NLS-1$
+					pckOut.writeString(
+							PACKET_UNSHALLOW + unshallow.name() + '\n');
 					unshallowCommits.add(unshallow);
-				}, Collections.emptyList());
+				}, deepenNots);
 				pckOut.end();
 			}
 
@@ -1109,7 +1119,7 @@ else if (requestValidator instanceof AnyRequestValidator)
 
 		if (sendPack) {
 			sendPack(accumulator, req, refs == null ? null : refs.values(),
-					unshallowCommits, Collections.emptyList(), pckOut);
+					unshallowCommits, deepenNots, pckOut);
 		}
 	}
 
@@ -1188,15 +1198,7 @@ private void fetchV2(PacketLineOut pckOut) throws IOException {
 
 		// TODO(ifrade): Refactor to pass around the Request object, instead of
 		// copying data back to class fields
-		List<ObjectId> deepenNots = new ArrayList<>();
-		for (String s : req.getDeepenNotRefs()) {
-			Ref ref = findRef(s);
-			if (ref == null) {
-				throw new PackProtocolException(MessageFormat
-						.format(JGitText.get().invalidRefName, s));
-			}
-			deepenNots.add(ref.getObjectId());
-		}
+		List<ObjectId> deepenNots = parseDeepenNots(req.getDeepenNots());
 
 		Map<String, ObjectId> wantedRefs = wantedRefs(req);
 		// TODO(ifrade): Avoid mutating the parsed request.
@@ -1206,7 +1208,7 @@ private void fetchV2(PacketLineOut pckOut) throws IOException {
 		boolean sectionSent = false;
 		boolean mayHaveShallow = req.getDepth() != 0
 				|| req.getDeepenSince() != 0
-				|| !req.getDeepenNotRefs().isEmpty();
+				|| !req.getDeepenNots().isEmpty();
 		List<ObjectId> shallowCommits = new ArrayList<>();
 		List<ObjectId> unshallowCommits = new ArrayList<>();
 
@@ -1232,7 +1234,7 @@ private void fetchV2(PacketLineOut pckOut) throws IOException {
 					GitProtocolConstants.SECTION_ACKNOWLEDGMENTS + '\n');
 			for (ObjectId id : req.getPeerHas()) {
 				if (walk.getObjectReader().has(id)) {
-					pckOut.writeString("ACK " + id.getName() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
+					pckOut.writeString(PACKET_ACK + id.getName() + '\n');
 				}
 			}
 			processHaveLines(req.getPeerHas(), ObjectId.zeroId(),
@@ -1250,12 +1252,13 @@ private void fetchV2(PacketLineOut pckOut) throws IOException {
 			if (mayHaveShallow) {
 				if (sectionSent)
 					pckOut.writeDelim();
-				pckOut.writeString("shallow-info\n"); //$NON-NLS-1$
+				pckOut.writeString(
+						GitProtocolConstants.SECTION_SHALLOW_INFO + '\n');
 				for (ObjectId o : shallowCommits) {
-					pckOut.writeString("shallow " + o.getName() + '\n'); //$NON-NLS-1$
+					pckOut.writeString(PACKET_SHALLOW + o.getName() + '\n');
 				}
 				for (ObjectId o : unshallowCommits) {
-					pckOut.writeString("unshallow " + o.getName() + '\n'); //$NON-NLS-1$
+					pckOut.writeString(PACKET_UNSHALLOW + o.getName() + '\n');
 				}
 				sectionSent = true;
 			}
@@ -1319,7 +1322,7 @@ private void objectInfo(PacketLineOut pckOut) throws IOException {
 						.format(JGitText.get().missingObject, oid.name()), e);
 			}
 
-			pckOut.writeString(oid.getName() + " " + size); //$NON-NLS-1$
+			pckOut.writeString(oid.getName() + ' ' + size);
 		}
 
 		pckOut.end();
@@ -1391,7 +1394,7 @@ private void serviceV2(PacketLineOut pckOut) throws IOException {
 			protocolV2Hook
 					.onCapabilities(CapabilitiesV2Request.builder().build());
 			for (String s : getV2CapabilityAdvertisement()) {
-				pckOut.writeString(s + "\n"); //$NON-NLS-1$
+				pckOut.writeString(s + '\n');
 			}
 			pckOut.end();
 
@@ -1618,7 +1621,7 @@ public void sendAdvertisedRefs(RefAdvertiser adv,
 	 */
 	public void sendMessage(String what) {
 		try {
-			msgOut.write(Constants.encode(what + "\n")); //$NON-NLS-1$
+			msgOut.write(Constants.encode(what + '\n'));
 		} catch (IOException e) {
 			// Ignore write failures.
 		}
@@ -1725,24 +1728,26 @@ private boolean negotiate(FetchRequest req,
 				if (commonBase.isEmpty() || multiAck != MultiAck.OFF)
 					pckOut.writeString("NAK\n"); //$NON-NLS-1$
 				if (noDone && sentReady) {
-					pckOut.writeString("ACK " + last.name() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
+					pckOut.writeString(PACKET_ACK + last.name() + '\n');
 					return true;
 				}
 				if (!biDirectionalPipe)
 					return false;
 				pckOut.flush();
 
-			} else if (line.startsWith("have ") && line.length() == 45) { //$NON-NLS-1$
-				peerHas.add(ObjectId.fromString(line.substring(5)));
+			} else if (line.startsWith(PACKET_HAVE)
+					&& line.length() == PACKET_HAVE.length() + 40) {
+				peerHas.add(ObjectId
+						.fromString(line.substring(PACKET_HAVE.length())));
 				accumulator.haves++;
-			} else if (line.equals("done")) { //$NON-NLS-1$
+			} else if (line.equals(PACKET_DONE)) {
 				last = processHaveLines(peerHas, last, pckOut, accumulator, Option.NONE);
 
 				if (commonBase.isEmpty())
 					pckOut.writeString("NAK\n"); //$NON-NLS-1$
 
 				else if (multiAck != MultiAck.OFF)
-					pckOut.writeString("ACK " + last.name() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
+					pckOut.writeString(PACKET_ACK + last.name() + '\n');
 
 				return true;
 
@@ -1803,14 +1808,15 @@ private ObjectId processHaveLines(List<ObjectId> peerHas, ObjectId last,
 				//
 				switch (multiAck) {
 				case OFF:
-					if (commonBase.size() == 1)
-						out.writeString("ACK " + obj.name() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
+					if (commonBase.size() == 1) {
+						out.writeString(PACKET_ACK + obj.name() + '\n');
+					}
 					break;
 				case CONTINUE:
-					out.writeString("ACK " + obj.name() + " continue\n"); //$NON-NLS-1$ //$NON-NLS-2$
+					out.writeString(PACKET_ACK + obj.name() + " continue\n"); //$NON-NLS-1$
 					break;
 				case DETAILED:
-					out.writeString("ACK " + obj.name() + " common\n"); //$NON-NLS-1$ //$NON-NLS-2$
+					out.writeString(PACKET_ACK + obj.name() + " common\n"); //$NON-NLS-1$
 					break;
 				}
 			}
@@ -1849,11 +1855,11 @@ private boolean shouldGiveUp(List<ObjectId> peerHas, PacketLineOut out, int miss
 							break;
 						case CONTINUE:
 							out.writeString(
-									"ACK " + id.name() + " continue\n"); //$NON-NLS-1$ //$NON-NLS-2$
+									PACKET_ACK + id.name() + " continue\n"); //$NON-NLS-1$
 							break;
 						case DETAILED:
 							out.writeString(
-									"ACK " + id.name() + " ready\n"); //$NON-NLS-1$ //$NON-NLS-2$
+									PACKET_ACK + id.name() + " ready\n"); //$NON-NLS-1$
 							readySent = true;
 							break;
 						}
@@ -1866,7 +1872,7 @@ private boolean shouldGiveUp(List<ObjectId> peerHas, PacketLineOut out, int miss
 		if (multiAck == MultiAck.DETAILED && !didOkToGiveUp
 				&& okToGiveUp()) {
 			ObjectId id = peerHas.get(peerHas.size() - 1);
-			out.writeString("ACK " + id.name() + " ready\n"); //$NON-NLS-1$ //$NON-NLS-2$
+			out.writeString(PACKET_ACK + id.name() + " ready\n"); //$NON-NLS-1$
 			readySent = true;
 		}
 
@@ -2038,7 +2044,8 @@ private static void checkNotAdvertisedWants(UploadPack up,
 							.filter(obj -> !(obj instanceof RevCommit))
 							.limit(1)
 							.collect(Collectors.toList()).get(0);
-					throw new WantNotValidException(nonCommit);
+					throw new WantNotValidException(nonCommit,
+							new Exception("Cannot walk without bitmaps")); //$NON-NLS-1$
 				}
 
 				try (ObjectWalk objWalk = walk.toObjectWalkWithSameObjects()) {
@@ -2052,6 +2059,11 @@ private static void checkNotAdvertisedWants(UploadPack up,
 					Optional<RevObject> unreachable = reachabilityChecker
 							.areAllReachable(wantsAsObjs, startersAsObjs);
 					if (unreachable.isPresent()) {
+						if (!repoHasBitmaps) {
+							throw new WantNotValidException(
+									unreachable.get(), new Exception(
+											"Retry with bitmaps enabled")); //$NON-NLS-1$
+						}
 						throw new WantNotValidException(unreachable.get());
 					}
 				}
@@ -2470,6 +2482,24 @@ private void addTagChain(
 		}
 	}
 
+	private List<ObjectId> parseDeepenNots(List<String> deepenNots)
+			throws IOException {
+		List<ObjectId> result = new ArrayList<>();
+		for (String s : deepenNots) {
+			if (ObjectId.isId(s)) {
+				result.add(ObjectId.fromString(s));
+			} else {
+				Ref ref = findRef(s);
+				if (ref == null) {
+					throw new PackProtocolException(MessageFormat
+							.format(JGitText.get().invalidRefName, s));
+				}
+				result.add(ref.getObjectId());
+			}
+		}
+		return result;
+	}
+
 	private static class ResponseBufferedOutputStream extends OutputStream {
 		private final OutputStream rawOut;
 
@@ -2533,7 +2563,7 @@ private class PackProtocolErrorWriter implements ErrorWriter {
 		@Override
 		public void writeError(String message) throws IOException {
 			new PacketLineOut(requireNonNull(rawOut))
-					.writeString("ERR " + message + '\n'); //$NON-NLS-1$
+					.writeString(PACKET_ERR + message + '\n');
 		}
 	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java
index 28b7423..2fd945b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java
@@ -56,7 +56,7 @@
 public class NameConflictTreeWalk extends TreeWalk {
 	private static final int TREE_MODE = FileMode.TREE.getBits();
 
-	private boolean fastMinHasMatch;
+	private boolean allTreesNamesMatchFastMinRef;
 
 	private AbstractTreeIterator dfConflict;
 
@@ -97,7 +97,7 @@ public NameConflictTreeWalk(ObjectReader or) {
 	AbstractTreeIterator min() throws CorruptObjectException {
 		for (;;) {
 			final AbstractTreeIterator minRef = fastMin();
-			if (fastMinHasMatch)
+			if (allTreesNamesMatchFastMinRef)
 				return minRef;
 
 			if (isTree(minRef)) {
@@ -118,15 +118,14 @@ AbstractTreeIterator min() throws CorruptObjectException {
 	}
 
 	private AbstractTreeIterator fastMin() {
-		fastMinHasMatch = true;
-
-		int i = 0;
+		int i = getFirstNonEofTreeIndex();
+		if (i == -1) {
+			// All trees reached the end.
+			allTreesNamesMatchFastMinRef = true;
+			return trees[trees.length - 1];
+		}
 		AbstractTreeIterator minRef = trees[i];
-		while (minRef.eof() && ++i < trees.length)
-			minRef = trees[i];
-		if (minRef.eof())
-			return minRef;
-
+		allTreesNamesMatchFastMinRef = true;
 		boolean hasConflict = false;
 		minRef.matches = minRef;
 		while (++i < trees.length) {
@@ -136,7 +135,7 @@ private AbstractTreeIterator fastMin() {
 
 			final int cmp = t.pathCompare(minRef);
 			if (cmp < 0) {
-				if (fastMinHasMatch && isTree(minRef) && !isTree(t)
+				if (allTreesNamesMatchFastMinRef && isTree(minRef) && !isTree(t)
 						&& nameEqual(minRef, t)) {
 					// We used to be at a tree, but now we are at a file
 					// with the same name. Allow the file to match the
@@ -145,7 +144,7 @@ && nameEqual(minRef, t)) {
 					t.matches = minRef;
 					hasConflict = true;
 				} else {
-					fastMinHasMatch = false;
+					allTreesNamesMatchFastMinRef = false;
 					t.matches = t;
 					minRef = t;
 				}
@@ -153,7 +152,7 @@ && nameEqual(minRef, t)) {
 				// Exact name/mode match is best.
 				//
 				t.matches = minRef;
-			} else if (fastMinHasMatch && isTree(t) && !isTree(minRef)
+			} else if (allTreesNamesMatchFastMinRef && isTree(t) && !isTree(minRef)
 					&& !isGitlink(minRef) && nameEqual(t, minRef)) {
 				// The minimum is a file (non-tree) but the next entry
 				// of this iterator is a tree whose name matches our file.
@@ -172,14 +171,23 @@ && nameEqual(minRef, t)) {
 				minRef = t;
 				hasConflict = true;
 			} else
-				fastMinHasMatch = false;
+				allTreesNamesMatchFastMinRef = false;
 		}
 
-		if (hasConflict && fastMinHasMatch && dfConflict == null)
+		if (hasConflict && allTreesNamesMatchFastMinRef && dfConflict == null)
 			dfConflict = minRef;
 		return minRef;
 	}
 
+	private int getFirstNonEofTreeIndex() {
+		for (int i = 0; i < trees.length; i++) {
+			if (!trees[i].eof()) {
+				return i;
+			}
+		}
+		return -1;
+	}
+
 	private static boolean nameEqual(final AbstractTreeIterator a,
 			final AbstractTreeIterator b) {
 		return a.pathCompare(b, TREE_MODE) == 0;
@@ -268,6 +276,28 @@ private AbstractTreeIterator combineDF(AbstractTreeIterator minRef)
 			}
 		}
 
+		// When the combineDF is called, the t.matches field stores other
+		// entry (i.e. tree iterator) with an equal path. However, the
+		// previous loop moves each iterator independently. As a result,
+		// iterators which have had equals path at the start of the
+		// method can have different paths at this point.
+		// Reevaluate existing matches.
+		// The NameConflictTreeWalkTest.testDF_specialFileNames test
+		// cover this situation.
+		for (AbstractTreeIterator t : trees) {
+			// The previous loop doesn't touch tree iterator if it matches
+			// minRef. Skip it here
+			if (t.eof() || t.matches == null || t.matches == minRef)
+				continue;
+			// The t.pathCompare takes into account the entry type (file
+			// or directory) and returns non-zero value if names match
+			// but entry type don't match.
+			// We want to keep such matches (file/directory conflict),
+			// so reset matches only if names are not equal.
+			if (!nameEqual(t, t.matches))
+				t.matches = null;
+		}
+
 		if (treeMatch != null) {
 			// If we do have a conflict use one of the directory
 			// matching iterators instead of the file iterator.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java
index 8269666..ece9452 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java
@@ -1375,6 +1375,14 @@ public void enterSubtree() throws MissingObjectException,
 		System.arraycopy(tmp, 0, trees, 0, trees.length);
 	}
 
+	/**
+	 * Returns an AbstractTreeIterator from {@code trees} with the smallest name, and sets its
+	 * {@code matches} field. This may clobber {@code matches} in other {@code tree}s. Other iterators
+	 * at the same name will have their {@code matches} pointing to the same {@code min()} value.
+	 *
+	 * @return the smallest tree iterator available.
+	 * @throws CorruptObjectException
+	 */
 	@SuppressWarnings("unused")
 	AbstractTreeIterator min() throws CorruptObjectException {
 		int i = 0;
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 427eac5..d8a61ec 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
@@ -1004,6 +1004,12 @@ public FileMode getIndexFileMode(DirCacheIterator indexIter) {
 			return wtMode;
 		}
 		final FileMode iMode = indexIter.getEntryFileMode();
+		if (iMode == FileMode.SYMLINK
+				&& getOptions().getSymLinks() == SymLinks.FALSE
+				&& (wtMode == FileMode.REGULAR_FILE
+						|| wtMode == FileMode.EXECUTABLE_FILE)) {
+			return iMode;
+		}
 		if (getOptions().isFileMode() && iMode != FileMode.GITLINK && iMode != FileMode.TREE) {
 			return wtMode;
 		}
@@ -1274,11 +1280,15 @@ private static class PerDirectoryIgnoreNode extends IgnoreNode {
 		}
 
 		IgnoreNode load() throws IOException {
-			IgnoreNode r = new IgnoreNode();
+			return load(null);
+		}
+
+		IgnoreNode load(IgnoreNode parent) throws IOException {
+			IgnoreNodeWithParent r = new IgnoreNodeWithParent(parent);
 			try (InputStream in = entry.openInputStream()) {
 				r.parse(name, in);
 			}
-			return r.getRules().isEmpty() ? null : r;
+			return r.getRules().isEmpty() && parent == null ? null : r;
 		}
 	}
 
@@ -1292,29 +1302,41 @@ private static class RootIgnoreNode extends PerDirectoryIgnoreNode {
 		}
 
 		@Override
-		IgnoreNode load() throws IOException {
-			IgnoreNode r;
-			if (entry != null) {
-				r = super.load();
-				if (r == null)
-					r = new IgnoreNode();
-			} else {
-				r = new IgnoreNode();
-			}
-
+		IgnoreNode load(IgnoreNode parent) throws IOException {
+			IgnoreNode coreExclude = new IgnoreNodeWithParent(parent);
 			FS fs = repository.getFS();
 			Path path = repository.getConfig().getPath(
 					ConfigConstants.CONFIG_CORE_SECTION, null,
 					ConfigConstants.CONFIG_KEY_EXCLUDESFILE, fs, null, null);
 			if (path != null) {
-				loadRulesFromFile(r, path.toFile());
+				loadRulesFromFile(coreExclude, path.toFile());
+			}
+			if (coreExclude.getRules().isEmpty()) {
+				coreExclude = parent;
 			}
 
+			IgnoreNode infoExclude = new IgnoreNodeWithParent(
+					coreExclude);
 			File exclude = fs.resolve(repository.getDirectory(),
 					Constants.INFO_EXCLUDE);
-			loadRulesFromFile(r, exclude);
+			loadRulesFromFile(infoExclude, exclude);
+			if (infoExclude.getRules().isEmpty()) {
+				infoExclude = null;
+			}
 
-			return r.getRules().isEmpty() ? null : r;
+			IgnoreNode parentNode = infoExclude != null ? infoExclude
+					: coreExclude;
+
+			IgnoreNode r;
+			if (entry != null) {
+				r = super.load(parentNode);
+				if (r == null) {
+					return null;
+				}
+			} else {
+				return parentNode;
+			}
+			return r.getRules().isEmpty() ? parentNode : r;
 		}
 
 		private static void loadRulesFromFile(IgnoreNode r, File exclude)
@@ -1327,6 +1349,24 @@ private static void loadRulesFromFile(IgnoreNode r, File exclude)
 		}
 	}
 
+	private static class IgnoreNodeWithParent extends IgnoreNode {
+
+		private final IgnoreNode parent;
+
+		IgnoreNodeWithParent(IgnoreNode parent) {
+			this.parent = parent;
+		}
+
+		@Override
+		public Boolean checkIgnored(String path, boolean isDirectory) {
+			Boolean result = super.checkIgnored(path, isDirectory);
+			if (result == null && parent != null) {
+				return parent.checkIgnored(path, isDirectory);
+			}
+			return result;
+		}
+	}
+
 	/** Magic type indicating we know rules exist, but they aren't loaded. */
 	private static class PerDirectoryAttributesNode extends AttributesNode {
 		final Entry entry;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/Paths.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/Paths.java
index 5a39f95..ae13ef7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/Paths.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/Paths.java
@@ -18,7 +18,8 @@
  *
  * @since 4.2
  */
-public class Paths {
+public final class Paths {
+
 	/**
 	 * Remove trailing {@code '/'} if present.
 	 *
@@ -43,6 +44,33 @@ public static String stripTrailingSeparator(String path) {
 	}
 
 	/**
+	 * Determines whether a git path {@code folder} is a prefix of another git
+	 * path {@code path}, or the same as {@code path}. An empty {@code folder}
+	 * is <em>not</em> not considered a prefix and matches only if {@code path}
+	 * is also empty.
+	 *
+	 * @param folder
+	 *            a git path for a directory, without trailing slash
+	 * @param path
+	 *            a git path
+	 * @return {@code true} if {@code folder} is a directory prefix of
+	 *         {@code path}, or is equal to {@code path}, {@code false}
+	 *         otherwise
+	 * @since 6.3
+	 */
+	public static boolean isEqualOrPrefix(String folder, String path) {
+		if (folder.isEmpty()) {
+			return path.isEmpty();
+		}
+		boolean isPrefix = path.startsWith(folder);
+		if (isPrefix) {
+			int length = folder.length();
+			return path.length() == length || path.charAt(length) == '/';
+		}
+		return false;
+	}
+
+	/**
 	 * Compare two paths according to Git path sort ordering rules.
 	 *
 	 * @param aPath
@@ -63,9 +91,8 @@ public static String stripTrailingSeparator(String path) {
 	 * @param bMode
 	 *            mode of the second file. Trees are sorted as though
 	 *            {@code bPath[bEnd] == '/'}, even if bEnd does not exist.
-	 * @return &lt;0 if {@code aPath} sorts before {@code bPath};
-	 *         0 if the paths are the same;
-	 *         &gt;0 if {@code aPath} sorts after {@code bPath}.
+	 * @return &lt;0 if {@code aPath} sorts before {@code bPath}; 0 if the paths
+	 *         are the same; &gt;0 if {@code aPath} sorts after {@code bPath}.
 	 */
 	public static int compare(byte[] aPath, int aPos, int aEnd, int aMode,
 			byte[] bPath, int bPos, int bEnd, int bMode) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
index 16e2577..5ced071 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
@@ -63,6 +63,8 @@ public abstract class SystemReader {
 
 	private static volatile Boolean isWindows;
 
+	private static volatile Boolean isLinux;
+
 	static {
 		SystemReader r = new Default();
 		r.init();
@@ -185,6 +187,7 @@ public static SystemReader getInstance() {
 	public static void setInstance(SystemReader newReader) {
 		isMacOS = null;
 		isWindows = null;
+		isLinux = null;
 		if (newReader == null)
 			INSTANCE = DEFAULT;
 		else {
@@ -543,6 +546,20 @@ public boolean isMacOS() {
 		return isMacOS.booleanValue();
 	}
 
+	/**
+	 * Whether we are running on Linux.
+	 *
+	 * @return true if we are running on Linux.
+	 * @since 6.3
+	 */
+	public boolean isLinux() {
+		if (isLinux == null) {
+			String osname = getOsName();
+			isLinux = Boolean.valueOf(osname.toLowerCase().startsWith("linux")); //$NON-NLS-1$
+		}
+		return isLinux.booleanValue();
+	}
+
 	private String getOsName() {
 		return AccessController.doPrivileged(
 				(PrivilegedAction<String>) () -> getProperty("os.name") //$NON-NLS-1$
diff --git a/pom.xml b/pom.xml
index 787f001..5693c10 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>6.2.1-SNAPSHOT</version>
+  <version>6.3.1-SNAPSHOT</version>
 
   <name>JGit - Parent</name>
   <url>${jgit-url}</url>
@@ -150,7 +150,7 @@
     <java.version>11</java.version>
     <bundle-manifest>${project.build.directory}/META-INF/MANIFEST.MF</bundle-manifest>
 
-    <jgit-last-release-version>6.1.0.202203080745-r</jgit-last-release-version>
+    <jgit-last-release-version>6.2.0.202206071550-r</jgit-last-release-version>
     <ant-version>1.10.12</ant-version>
     <apache-sshd-version>2.8.0</apache-sshd-version>
     <jsch-version>0.1.55</jsch-version>
@@ -170,7 +170,7 @@
     <maven-javadoc-plugin-version>3.3.1</maven-javadoc-plugin-version>
     <tycho-extras-version>2.6.0</tycho-extras-version>
     <gson-version>2.8.9</gson-version>
-    <bouncycastle-version>1.70</bouncycastle-version>
+    <bouncycastle-version>1.71</bouncycastle-version>
     <spotbugs-maven-plugin-version>4.3.0</spotbugs-maven-plugin-version>
     <maven-project-info-reports-plugin-version>3.1.2</maven-project-info-reports-plugin-version>
     <maven-jxr-plugin-version>3.1.1</maven-jxr-plugin-version>
@@ -750,25 +750,25 @@
 
       <dependency>
         <groupId>org.bouncycastle</groupId>
-        <artifactId>bcpg-jdk15on</artifactId>
+        <artifactId>bcpg-jdk18on</artifactId>
         <version>${bouncycastle-version}</version>
       </dependency>
 
       <dependency>
         <groupId>org.bouncycastle</groupId>
-        <artifactId>bcprov-jdk15on</artifactId>
+        <artifactId>bcprov-jdk18on</artifactId>
         <version>${bouncycastle-version}</version>
       </dependency>
 
       <dependency>
         <groupId>org.bouncycastle</groupId>
-        <artifactId>bcutil-jdk15on</artifactId>
+        <artifactId>bcutil-jdk18on</artifactId>
         <version>${bouncycastle-version}</version>
       </dependency>
 
       <dependency>
         <groupId>org.bouncycastle</groupId>
-        <artifactId>bcpkix-jdk15on</artifactId>
+        <artifactId>bcpkix-jdk18on</artifactId>
         <version>${bouncycastle-version}</version>
       </dependency>