Merge branch 'stable-6.4' into stable-6.5

* stable-6.4:
  Checkout: better directory handling

Change-Id: I68e11fb06a354961f4146de51d326b69be5b69d3
diff --git a/.bazelrc b/.bazelrc
index 8c32661..e6c3dc6 100644
--- a/.bazelrc
+++ b/.bazelrc
@@ -30,6 +30,7 @@
 
 test --build_tests_only
 test --test_output=errors
+test --flaky_test_attempts=3
 
 import %workspace%/tools/remote-bazelrc
 
diff --git a/.gitignore b/.gitignore
index 553ecac..5306e41 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
 /.project
+/org.eclipse.jgit.benchmarks/dependency-reduced-pom.xml
 /target
 infer-out
 bazel-*
diff --git a/Documentation/config-options.md b/Documentation/config-options.md
index 815677c..38ce324 100644
--- a/Documentation/config-options.md
+++ b/Documentation/config-options.md
@@ -100,6 +100,7 @@
 | `pack.deltaCompression` | `true` | ⃞ | Whether the writer will create new deltas on the fly. `true` if the pack writer will create a new delta when either `pack.reuseDeltas` is false, or no suitable delta is available for reuse. |
 | `pack.depth` | `50` | ✅ | Maximum depth of delta chain set up for the pack writer. |
 | `pack.indexVersion` | `2` | ✅ | Pack index file format version. |
+| `pack.minBytesForObjSizeIndex` | `-1` | ⃞ | Minimum size of an object (inclusive, in bytes) to be included in the size index. -1 to disable the object size index. |
 | `pack.minSizePreventRacyPack` | `100 MiB` | ⃞ | Minimum packfile size for which we wait before opening a newly written pack to prevent its lastModified timestamp could be racy if `pack.waitPreventRacyPack` is `true`. |
 | `pack.preserveOldPacks` | `false` | ⃞ | Whether to preserve old packs during gc in the `objects/pack/preserved` directory. This can avoid rare races between gc removing pack files and other concurrent operations. If this option is false data loss can occur in rare cases when an object is believed to be unreferenced when object repacking is running, and then garbage collection deletes it while another concurrent operation references this object shortly before garbage collection deletes it. When this happens, a new reference is created which points to a now missing object. |
 | `pack.prunePreserved` | `false` | ⃞ | Whether to prune preserved pack files from the previous run of gc from the `objects/pack/preserved` directory. This helps to limit the additional storage space needed to preserve old packs when `pack.preserveOldPacks = true`. |
diff --git a/WORKSPACE b/WORKSPACE
index ceb0e48..8e7b81c 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -80,14 +80,14 @@
 
 maven_jar(
     name = "httpclient",
-    artifact = "org.apache.httpcomponents:httpclient:4.5.13",
-    sha1 = "e5f6cae5ca7ecaac1ec2827a9e2d65ae2869cada",
+    artifact = "org.apache.httpcomponents:httpclient:4.5.14",
+    sha1 = "1194890e6f56ec29177673f2f12d0b8e627dec98",
 )
 
 maven_jar(
     name = "httpcore",
-    artifact = "org.apache.httpcomponents:httpcore:4.4.15",
-    sha1 = "7f2e0c573eaa7a74bac2e89b359e1f73d92a0a1d",
+    artifact = "org.apache.httpcomponents:httpcore:4.4.16",
+    sha1 = "51cf043c87253c9f58b539c9f7e44c8894223850",
 )
 
 SSHD_VERS = "2.9.2"
@@ -148,8 +148,8 @@
 
 maven_jar(
     name = "commons-compress",
-    artifact = "org.apache.commons:commons-compress:1.21",
-    sha1 = "4ec95b60d4e86b5c95a0e919cb172a0af98011ef",
+    artifact = "org.apache.commons:commons-compress:1.22",
+    sha1 = "691a8b4e6cf4248c3bc72c8b719337d5cb7359fa",
 )
 
 maven_jar(
@@ -210,59 +210,59 @@
 
 maven_jar(
     name = "gson",
-    artifact = "com.google.code.gson:gson:2.9.1",
-    sha1 = "02cc2131b98ebfb04e2b2c7dfb84431f4045096b",
+    artifact = "com.google.code.gson:gson:2.10.1",
+    sha1 = "b3add478d4382b78ea20b1671390a858002feb6c",
 )
 
-JETTY_VER = "10.0.6"
+JETTY_VER = "10.0.13"
 
 maven_jar(
     name = "jetty-servlet",
     artifact = "org.eclipse.jetty:jetty-servlet:" + JETTY_VER,
-    sha1 = "482165726bf54dd10ee7e2aeb4ae9481eee0c878",
-    src_sha1 = "8a8173a0bc6c0d215fc9fb9ba5fd50bae1690f9c",
+    sha1 = "a6ee6e48e98377863aa80f41ea979df678b17966",
+    src_sha1 = "5a01db2e1bae632879e9b90e845ae946059b46c9",
 )
 
 maven_jar(
     name = "jetty-security",
     artifact = "org.eclipse.jetty:jetty-security:" + JETTY_VER,
-    sha1 = "513f44ed9636ca5e0adefa0c0b81511065dfddd2",
-    src_sha1 = "2e7eb2edbf1592e15b338096651e379fea860859",
+    sha1 = "6d4c88cf068709d9f2499ca417b23f3f835b0c43",
+    src_sha1 = "887e7a7c457e149df9c23db89c7d2425c4444ccf",
 )
 
 maven_jar(
     name = "jetty-server",
     artifact = "org.eclipse.jetty:jetty-server:" + JETTY_VER,
-    sha1 = "125ee07e4d8182a6afca00d543f6a4dcc84f2678",
-    src_sha1 = "5c0789872ec6743ae893131ae81262aaefc87fe6",
+    sha1 = "f472705ebfce7e9a5b6cb8cbb84e73767e35fad7",
+    src_sha1 = "2689f8e616282b19f34d43f89800f67490ae65fa",
 )
 
 maven_jar(
     name = "jetty-http",
     artifact = "org.eclipse.jetty:jetty-http:" + JETTY_VER,
-    sha1 = "4c8eed25d577002a6c0f9f3ef340eb581390f696",
-    src_sha1 = "ac7214d6202ee0cbc4bdbcf90c7906ca716e84e5",
+    sha1 = "b3dc7ec1da090106031dd36cb1e2637a7fb6ce1c",
+    src_sha1 = "1bbb620e34218584bfdf11542e2b46781437335d",
 )
 
 maven_jar(
     name = "jetty-io",
     artifact = "org.eclipse.jetty:jetty-io:" + JETTY_VER,
-    sha1 = "1ab82ae5dfdbb07f0ffa07f28274fdf30e3e96ee",
-    src_sha1 = "c59082f3a09c024fafc281f432b67432d398b8c0",
+    sha1 = "be9d7f226022b02e174a83d597d088e22e12d365",
+    src_sha1 = "48f5b1ce8570a9d560e62c39170e754288a1d290",
 )
 
 maven_jar(
     name = "jetty-util",
     artifact = "org.eclipse.jetty:jetty-util:" + JETTY_VER,
-    sha1 = "4e2935749ea1c9fcabba61a857f8283c7f5f9885",
-    src_sha1 = "6baba651899c044e14ba37d43934950670d2aa4e",
+    sha1 = "35caf3afb3cca22ca4bc36908bf82e6d973c5be4",
+    src_sha1 = "9d7c19deb76c0247ad0d25afce6e4c0d681d2af0",
 )
 
 maven_jar(
     name = "jetty-util-ajax",
     artifact = "org.eclipse.jetty:jetty-util-ajax:" + JETTY_VER,
-    sha1 = "a801d4b5f5e906f134713ae82fd1ea10a15902c6",
-    src_sha1 = "f35f5525a5d30dc1237b85457d758d578e3ce8d0",
+    sha1 = "bc52bc38cb76b5c260ec109661ebcb02393d83a7",
+    src_sha1 = "b229198672cfb765ce7571e5e0e855e01170f881",
 )
 
 BOUNCYCASTLE_VER = "1.72"
diff --git a/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF
index 75d80a2..3ed4d95 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.4.1.qualifier
+Bundle-Version: 6.5.1.qualifier
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: JavaSE-11
 Import-Package: org.apache.tools.ant,
- org.eclipse.jgit.ant.tasks;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.junit;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.lib;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.util;version="[6.4.1,6.5.0)",
+ org.eclipse.jgit.ant.tasks;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.junit;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.lib;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.util;version="[6.5.1,6.6.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 ac65936..61e1b57 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.4.1-SNAPSHOT</version>
+    <version>6.5.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 0bebf8e..1cb6aca 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.4.1.qualifier
+Bundle-Version: 6.5.1.qualifier
 Bundle-RequiredExecutionEnvironment: JavaSE-11
 Import-Package: org.apache.tools.ant,
-  org.eclipse.jgit.storage.file;version="[6.4.1,6.5.0)"
+  org.eclipse.jgit.storage.file;version="[6.5.1,6.6.0)"
 Bundle-Localization: plugin
 Bundle-Vendor: %Bundle-Vendor
-Export-Package: org.eclipse.jgit.ant;version="6.4.1",
- org.eclipse.jgit.ant.tasks;version="6.4.1";
+Export-Package: org.eclipse.jgit.ant;version="6.5.1",
+ org.eclipse.jgit.ant.tasks;version="6.5.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 1d0b760..9dc6f64 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.4.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.ant;version="6.4.1.qualifier";roots="."
+Bundle-Version: 6.5.1.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.ant;version="6.5.1.qualifier";roots="."
diff --git a/org.eclipse.jgit.ant/pom.xml b/org.eclipse.jgit.ant/pom.xml
index 8110b0f..df42c00 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.4.1-SNAPSHOT</version>
+    <version>6.5.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 d186e7b..446b1d5 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.4.1.qualifier
+Bundle-Version: 6.5.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.4.1,6.5.0)",
- org.eclipse.jgit.lib;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.nls;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.revwalk;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.util;version="[6.4.1,6.5.0)",
+ org.eclipse.jgit.api;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.lib;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.nls;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.revwalk;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.util;version="[6.5.1,6.6.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.4.1";
+Export-Package: org.eclipse.jgit.archive;version="6.5.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.4.1";x-internal:=true
+ org.eclipse.jgit.archive.internal;version="6.5.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 590b30b..2a1ba49 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.4.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.archive;version="6.4.1.qualifier";roots="."
+Bundle-Version: 6.5.1.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.archive;version="6.5.1.qualifier";roots="."
diff --git a/org.eclipse.jgit.archive/pom.xml b/org.eclipse.jgit.archive/pom.xml
index 4a7265d..615b932 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.4.1-SNAPSHOT</version>
+    <version>6.5.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 3998b65..ae9c6a3 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.4.1-SNAPSHOT</version>
+  <version>6.5.1-SNAPSHOT</version>
   <artifactId>org.eclipse.jgit.benchmarks</artifactId>
   <packaging>jar</packaging>
 
@@ -56,7 +56,7 @@
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-enforcer-plugin</artifactId>
-        <version>3.0.0-M3</version>
+        <version>3.1.0</version>
         <executions>
           <execution>
             <id>enforce-maven</id>
@@ -76,7 +76,7 @@
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-compiler-plugin</artifactId>
-        <version>3.8.1</version>
+        <version>3.10.1</version>
         <configuration>
           <encoding>UTF-8</encoding>
           <release>${java.version}</release>
@@ -113,7 +113,7 @@
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-shade-plugin</artifactId>
-        <version>3.2.4</version>
+        <version>3.4.1</version>
         <executions>
           <execution>
             <phase>package</phase>
@@ -175,29 +175,29 @@
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-site-plugin</artifactId>
-          <version>3.9.1</version>
+          <version>3.12.1</version>
           <dependencies>
             <dependency><!-- add support for ssh/scp -->
               <groupId>org.apache.maven.wagon</groupId>
-              <artifactId>wagon-ssh</artifactId>
-              <version>3.5.2</version>
+              <artifactId>wagon-ssh-external</artifactId>
+              <version>3.5.3</version>
             </dependency>
           </dependencies>
         </plugin>
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-surefire-report-plugin</artifactId>
-          <version>3.0.0-M5</version>
+          <version>3.0.0-M8</version>
         </plugin>
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-jxr-plugin</artifactId>
-          <version>3.1.1</version>
+          <version>3.3.0</version>
         </plugin>
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-project-info-reports-plugin</artifactId>
-          <version>3.1.1</version>
+          <version>3.4.2</version>
         </plugin>
       </plugins>
     </pluginManagement>
diff --git a/org.eclipse.jgit.coverage/pom.xml b/org.eclipse.jgit.coverage/pom.xml
index 7518dd9..34d2d7f 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.4.1-SNAPSHOT</version>
+    <version>6.5.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.4.1-SNAPSHOT</version>
+      <version>6.5.1-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.ant</artifactId>
-      <version>6.4.1-SNAPSHOT</version>
+      <version>6.5.1-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.archive</artifactId>
-      <version>6.4.1-SNAPSHOT</version>
+      <version>6.5.1-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.http.apache</artifactId>
-      <version>6.4.1-SNAPSHOT</version>
+      <version>6.5.1-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.http.server</artifactId>
-      <version>6.4.1-SNAPSHOT</version>
+      <version>6.5.1-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.lfs</artifactId>
-      <version>6.4.1-SNAPSHOT</version>
+      <version>6.5.1-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.lfs.server</artifactId>
-      <version>6.4.1-SNAPSHOT</version>
+      <version>6.5.1-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.pgm</artifactId>
-      <version>6.4.1-SNAPSHOT</version>
+      <version>6.5.1-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.ui</artifactId>
-      <version>6.4.1-SNAPSHOT</version>
+      <version>6.5.1-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.ssh.apache</artifactId>
-      <version>6.4.1-SNAPSHOT</version>
+      <version>6.5.1-SNAPSHOT</version>
     </dependency>
 
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.test</artifactId>
-      <version>6.4.1-SNAPSHOT</version>
+      <version>6.5.1-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.ant.test</artifactId>
-      <version>6.4.1-SNAPSHOT</version>
+      <version>6.5.1-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.http.test</artifactId>
-      <version>6.4.1-SNAPSHOT</version>
+      <version>6.5.1-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.pgm.test</artifactId>
-      <version>6.4.1-SNAPSHOT</version>
+      <version>6.5.1-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.lfs.test</artifactId>
-      <version>6.4.1-SNAPSHOT</version>
+      <version>6.5.1-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.lfs.server.test</artifactId>
-      <version>6.4.1-SNAPSHOT</version>
+      <version>6.5.1-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.ssh.apache.test</artifactId>
-      <version>6.4.1-SNAPSHOT</version>
+      <version>6.5.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 3fc9043..175eaba 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.4.1.qualifier
+Bundle-Version: 6.5.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.4.1,6.5.0)",
- org.eclipse.jgit.gpg.bc.internal.keys;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.util.sha1;version="[6.4.1,6.5.0)",
+ org.eclipse.jgit.gpg.bc.internal;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.gpg.bc.internal.keys;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.util.sha1;version="[6.5.1,6.6.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 05a407b..9217592 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.4.1-SNAPSHOT</version>
+    <version>6.5.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 dba21ee..23ff36d 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.4.1,6.5.0)"
+Fragment-Host: org.eclipse.jgit;bundle-version="[6.5.1,6.6.0)"
 Bundle-Vendor: %Bundle-Vendor
 Bundle-Localization: plugin
-Bundle-Version: 6.4.1.qualifier
+Bundle-Version: 6.5.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.4.1,6.5.0)",
- org.eclipse.jgit.api.errors;version="[6.4.1,6.5.0)",
+ org.eclipse.jgit.annotations;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.api.errors;version="[6.5.1,6.6.0)",
  org.slf4j;version="[1.7.0,2.0.0)"
-Export-Package: org.eclipse.jgit.gpg.bc;version="6.4.1",
- org.eclipse.jgit.gpg.bc.internal;version="6.4.1";x-friends:="org.eclipse.jgit.gpg.bc.test",
- org.eclipse.jgit.gpg.bc.internal.keys;version="6.4.1";x-friends:="org.eclipse.jgit.gpg.bc.test"
+Export-Package: org.eclipse.jgit.gpg.bc;version="6.5.1",
+ org.eclipse.jgit.gpg.bc.internal;version="6.5.1";x-friends:="org.eclipse.jgit.gpg.bc.test",
+ org.eclipse.jgit.gpg.bc.internal.keys;version="6.5.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 040c4c9..6604381 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.4.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.gpg.bc;version="6.4.1.qualifier";roots="."
+Bundle-Version: 6.5.1.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.gpg.bc;version="6.5.1.qualifier";roots="."
diff --git a/org.eclipse.jgit.gpg.bc/pom.xml b/org.eclipse.jgit.gpg.bc/pom.xml
index bf3b015..59484ac 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.4.1-SNAPSHOT</version>
+    <version>6.5.1-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.gpg.bc</artifactId>
diff --git a/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF
index b7fd8ec..0d798b6 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.4.1.qualifier
+Bundle-Version: 6.5.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.4.1,6.5.0)",
- org.eclipse.jgit.nls;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.transport.http;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.util;version="[6.4.1,6.5.0)"
-Export-Package: org.eclipse.jgit.transport.http.apache;version="6.4.1";
+ org.eclipse.jgit.annotations;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.nls;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.transport.http;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.util;version="[6.5.1,6.6.0)"
+Export-Package: org.eclipse.jgit.transport.http.apache;version="6.5.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 16fdf72..4556a0e 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.4.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.http.apache;version="6.4.1.qualifier";roots="."
+Bundle-Version: 6.5.1.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.http.apache;version="6.5.1.qualifier";roots="."
diff --git a/org.eclipse.jgit.http.apache/pom.xml b/org.eclipse.jgit.http.apache/pom.xml
index 454b01b..d9a3370 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.4.1-SNAPSHOT</version>
+    <version>6.5.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 41a1fd7..4fd2f50 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.4.1.qualifier
+Bundle-Version: 6.5.1.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %Bundle-Vendor
-Export-Package: org.eclipse.jgit.http.server;version="6.4.1",
- org.eclipse.jgit.http.server.glue;version="6.4.1";
+Export-Package: org.eclipse.jgit.http.server;version="6.5.1",
+ org.eclipse.jgit.http.server.glue;version="6.5.1";
   uses:="javax.servlet,javax.servlet.http",
- org.eclipse.jgit.http.server.resolver;version="6.4.1";
+ org.eclipse.jgit.http.server.resolver;version="6.5.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.4.1,6.5.0)",
- org.eclipse.jgit.errors;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.internal.storage.dfs;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.internal.storage.file;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.internal.transport.parser;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.lib;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.nls;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.revwalk;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.transport;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.transport.resolver;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.util;version="[6.4.1,6.5.0)"
+ org.eclipse.jgit.annotations;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.errors;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.internal.storage.file;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.internal.transport.parser;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.lib;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.nls;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.revwalk;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.transport;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.transport.resolver;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.util;version="[6.5.1,6.6.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 023b15c..f51e928 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.4.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.http.server;version="6.4.1.qualifier";roots="."
+Bundle-Version: 6.5.1.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.http.server;version="6.5.1.qualifier";roots="."
diff --git a/org.eclipse.jgit.http.server/pom.xml b/org.eclipse.jgit.http.server/pom.xml
index 3c98883..ee395ae 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.4.1-SNAPSHOT</version>
+    <version>6.5.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 8347693..e8beaab 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.4.1.qualifier
+Bundle-Version: 6.5.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.4.1,6.5.0)",
- org.eclipse.jgit.errors;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.http.server;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.http.server.glue;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.http.server.resolver;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.internal;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.internal.storage.dfs;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.internal.storage.file;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.internal.storage.reftable;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.junit;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.junit.http;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.lib;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.nls;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.revwalk;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.storage.file;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.transport;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.transport.http;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.transport.http.apache;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.transport.resolver;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.util;version="[6.4.1,6.5.0)",
+ org.eclipse.jgit.api;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.errors;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.http.server;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.http.server.glue;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.http.server.resolver;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.internal;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.internal.storage.file;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.internal.storage.reftable;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.junit;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.junit.http;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.lib;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.nls;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.revwalk;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.storage.file;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.transport;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.transport.http;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.transport.http.apache;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.transport.resolver;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.util;version="[6.5.1,6.6.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 39a11fa..70ddc71 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.4.1-SNAPSHOT</version>
+    <version>6.5.1-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.http.test</artifactId>
diff --git a/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF
index 5b5534f..19aa767 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.4.1.qualifier
+Bundle-Version: 6.5.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.4.1,6.5.0)",
- org.eclipse.jgit.http.server;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.internal.storage.file;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.junit;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.lib;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.revwalk;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.transport;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.transport.resolver;version="[6.4.1,6.5.0)",
+ org.eclipse.jgit.errors;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.http.server;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.internal.storage.file;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.junit;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.lib;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.revwalk;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.transport;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.transport.resolver;version="[6.5.1,6.6.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.4.1";
+Export-Package: org.eclipse.jgit.junit.http;version="6.5.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 416d9b7..13755c7 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.4.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.junit.http;version="6.4.1.qualifier";roots="."
+Bundle-Version: 6.5.1.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.junit.http;version="6.5.1.qualifier";roots="."
diff --git a/org.eclipse.jgit.junit.http/pom.xml b/org.eclipse.jgit.junit.http/pom.xml
index 8ebd77d..b1699d5 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.4.1-SNAPSHOT</version>
+    <version>6.5.1-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.junit.http</artifactId>
diff --git a/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF
index 79684e5..15df0bb 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.4.1.qualifier
+Bundle-Version: 6.5.1.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %Bundle-Vendor
 Bundle-ActivationPolicy: lazy
@@ -33,16 +33,16 @@
  org.apache.sshd.server.subsystem;version="[2.9.2,2.10.0)",
  org.apache.sshd.sftp;version="[2.9.2,2.10.0)",
  org.apache.sshd.sftp.server;version="[2.9.2,2.10.0)",
- org.eclipse.jgit.annotations;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.api;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.api.errors;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.errors;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.junit;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.lib;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.revwalk;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.transport;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.util;version="[6.4.1,6.5.0)",
+ org.eclipse.jgit.annotations;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.api;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.api.errors;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.errors;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.junit;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.lib;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.revwalk;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.transport;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.util;version="[6.5.1,6.6.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.4.1"
+Export-Package: org.eclipse.jgit.junit.ssh;version="6.5.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 a1f99de..a0b8e72 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.4.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.junit.ssh;version="6.4.1.qualifier";roots="."
+Bundle-Version: 6.5.1.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.junit.ssh;version="6.5.1.qualifier";roots="."
diff --git a/org.eclipse.jgit.junit.ssh/pom.xml b/org.eclipse.jgit.junit.ssh/pom.xml
index 9fb3d31..50e6e84 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.4.1-SNAPSHOT</version>
+    <version>6.5.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 320f4a7..479035c 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.4.1.qualifier
+Bundle-Version: 6.5.1.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %Bundle-Vendor
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: JavaSE-11
-Import-Package: org.eclipse.jgit.annotations;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.api;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.api.errors;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.dircache;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.errors;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.internal.storage.file;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.internal.storage.pack;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.lib;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.merge;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.revwalk;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.storage.file;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.transport;version="6.4.1",
- org.eclipse.jgit.treewalk;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.treewalk.filter;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.util;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.util.io;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.util.time;version="[6.4.1,6.5.0)",
+Import-Package: org.eclipse.jgit.annotations;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.api;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.api.errors;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.dircache;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.errors;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.internal.storage.file;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.lib;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.merge;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.revwalk;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.storage.file;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.transport;version="6.5.1",
+ org.eclipse.jgit.treewalk;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.treewalk.filter;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.util;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.util.io;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.util.time;version="[6.5.1,6.6.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.4.1";
+Export-Package: org.eclipse.jgit.junit;version="6.5.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.4.1";uses:="org.eclipse.jgit.util.time"
+ org.eclipse.jgit.junit.time;version="6.5.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 367787c..388479a 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.4.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.junit;version="6.4.1.qualifier";roots="."
+Bundle-Version: 6.5.1.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.junit;version="6.5.1.qualifier";roots="."
diff --git a/org.eclipse.jgit.junit/pom.xml b/org.eclipse.jgit.junit/pom.xml
index 0a69906..a320406 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.4.1-SNAPSHOT</version>
+    <version>6.5.1-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.junit</artifactId>
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/StrictWorkMonitor.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/StrictWorkMonitor.java
index 96d7aac..c77bca9 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/StrictWorkMonitor.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/StrictWorkMonitor.java
@@ -50,4 +50,9 @@
 	public boolean isCancelled() {
 		return false;
 	}
+
+	@Override
+	public void showDuration(boolean enabled) {
+		// not implemented
+	}
 }
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 7b3d7e0..af52de0 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.4.1.qualifier
+Bundle-Version: 6.5.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.4.1,6.5.0)",
- org.eclipse.jgit.api.errors;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.internal.storage.file;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.junit;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.junit.http;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.lfs;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.lfs.errors;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.lfs.lib;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.lfs.server;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.lfs.server.fs;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.lfs.test;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.lib;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.revwalk;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.storage.file;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.transport;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.treewalk;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.treewalk.filter;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.util;version="[6.4.1,6.5.0)",
+ org.eclipse.jgit.api;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.api.errors;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.internal.storage.file;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.junit;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.junit.http;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.lfs;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.lfs.errors;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.lfs.lib;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.lfs.server;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.lfs.server.fs;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.lfs.test;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.lib;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.revwalk;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.storage.file;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.transport;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.treewalk;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.treewalk.filter;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.util;version="[6.5.1,6.6.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 b0f6a5b..747f06a 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.4.1-SNAPSHOT</version>
+    <version>6.5.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 e27cc90..20b9d10 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.4.1.qualifier
+Bundle-Version: 6.5.1.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %Bundle-Vendor
-Export-Package: org.eclipse.jgit.lfs.server;version="6.4.1";
+Export-Package: org.eclipse.jgit.lfs.server;version="6.5.1";
   uses:="javax.servlet.http,
    org.eclipse.jgit.lfs.lib",
- org.eclipse.jgit.lfs.server.fs;version="6.4.1";
+ org.eclipse.jgit.lfs.server.fs;version="6.5.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.4.1";x-internal:=true,
- org.eclipse.jgit.lfs.server.s3;version="6.4.1";
+ org.eclipse.jgit.lfs.server.internal;version="6.5.1";x-internal:=true,
+ org.eclipse.jgit.lfs.server.s3;version="6.5.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.4.1,6.5.0)",
- org.eclipse.jgit.internal;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.internal.storage.file;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.lfs.errors;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.lfs.internal;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.lfs.lib;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.lib;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.nls;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.transport.http;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.transport.http.apache;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.util;version="[6.4.1,6.5.0)",
+ org.eclipse.jgit.annotations;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.internal;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.internal.storage.file;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.lfs.errors;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.lfs.internal;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.lfs.lib;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.lib;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.nls;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.transport.http;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.transport.http.apache;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.util;version="[6.5.1,6.6.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 595a04e..93eeea5 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.4.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.lfs.server;version="6.4.1.qualifier";roots="."
+Bundle-Version: 6.5.1.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.lfs.server;version="6.5.1.qualifier";roots="."
diff --git a/org.eclipse.jgit.lfs.server/pom.xml b/org.eclipse.jgit.lfs.server/pom.xml
index 69336a6..6b99508 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.4.1-SNAPSHOT</version>
+    <version>6.5.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 8facd81..c1cdf3b 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.4.1.qualifier
+Bundle-Version: 6.5.1.qualifier
 Bundle-Vendor: %Bundle-Vendor
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-11
-Import-Package: org.eclipse.jgit.api;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.attributes;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.internal.storage.dfs;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.junit;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.lfs;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.lfs.errors;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.lfs.internal;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.lfs.lib;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.lib;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.revwalk;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.transport;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.transport.http;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.treewalk;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.treewalk.filter;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.util;version="[6.4.1,6.5.0)",
+Import-Package: org.eclipse.jgit.api;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.attributes;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.junit;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.lfs;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.lfs.errors;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.lfs.internal;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.lfs.lib;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.lib;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.revwalk;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.transport;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.transport.http;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.treewalk;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.treewalk.filter;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.util;version="[6.5.1,6.6.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.4.1";x-friends:="org.eclipse.jgit.lfs.server.test"
+Export-Package: org.eclipse.jgit.lfs.test;version="6.5.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 43a5d08..a30bd54 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.4.1-SNAPSHOT</version>
+    <version>6.5.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 f951183..fb8993b 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.4.1.qualifier
+Bundle-Version: 6.5.1.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %Bundle-Vendor
-Export-Package: org.eclipse.jgit.lfs;version="6.4.1",
- org.eclipse.jgit.lfs.errors;version="6.4.1",
- org.eclipse.jgit.lfs.internal;version="6.4.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.4.1"
+Export-Package: org.eclipse.jgit.lfs;version="6.5.1",
+ org.eclipse.jgit.lfs.errors;version="6.5.1",
+ org.eclipse.jgit.lfs.internal;version="6.5.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.5.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.4.1,6.5.0)";resolution:=optional,
- org.eclipse.jgit.api.errors;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.attributes;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.diff;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.dircache;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.errors;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.hooks;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.internal.storage.file;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.lib;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.nls;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.revwalk;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.storage.file;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.storage.pack;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.transport;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.transport.http;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.treewalk;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.treewalk.filter;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.util;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.util.io;version="[6.4.1,6.5.0)"
+ org.eclipse.jgit.annotations;version="[6.5.1,6.6.0)";resolution:=optional,
+ org.eclipse.jgit.api.errors;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.attributes;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.diff;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.dircache;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.errors;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.hooks;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.internal.storage.file;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.lib;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.nls;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.revwalk;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.storage.file;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.storage.pack;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.transport;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.transport.http;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.treewalk;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.treewalk.filter;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.util;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.util.io;version="[6.5.1,6.6.0)"
diff --git a/org.eclipse.jgit.lfs/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.lfs/META-INF/SOURCE-MANIFEST.MF
index 62dad42..8525bf5 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.4.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.lfs;version="6.4.1.qualifier";roots="."
+Bundle-Version: 6.5.1.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.lfs;version="6.5.1.qualifier";roots="."
diff --git a/org.eclipse.jgit.lfs/pom.xml b/org.eclipse.jgit.lfs/pom.xml
index 18942f0..0279b22 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.4.1-SNAPSHOT</version>
+    <version>6.5.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 7930aa1..062b3a4 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.4.1.qualifier"
+      version="6.5.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 93f42ab..2130178 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.4.1-SNAPSHOT</version>
+    <version>6.5.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 5acf8ff..74758c6 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.4.1.qualifier"
+      version="6.5.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.4.1" match="equivalent"/>
+      <import plugin="org.eclipse.jgit" version="6.5.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 bf6fe5d..ccf9325 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.4.1-SNAPSHOT</version>
+    <version>6.5.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 85ff865..415b111 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.4.1.qualifier"
+      version="6.5.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.4.1" match="equivalent"/>
+      <import plugin="org.eclipse.jgit" version="6.5.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 a1f6f0f..d48944d 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.4.1-SNAPSHOT</version>
+    <version>6.5.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 55fdba6..5d3b1c4 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.4.1.qualifier"
+      version="6.5.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.4.1" match="equivalent"/>
+      <import plugin="org.eclipse.jgit" version="6.5.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 88b2cd2..4f3be7a 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.4.1-SNAPSHOT</version>
+    <version>6.5.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 4ef4c5d..beb0f9b 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.4.1.qualifier"
+      version="6.5.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.4.1" match="equivalent"/>
+      <import feature="org.eclipse.jgit" version="6.5.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 9a4d7fd..35276dd 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.4.1-SNAPSHOT</version>
+    <version>6.5.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 6573c6f..6ccd100 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.4.1.qualifier"
+      version="6.5.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.4.1" match="equivalent"/>
-      <import feature="org.eclipse.jgit.lfs" version="6.4.1" match="equivalent"/>
-      <import feature="org.eclipse.jgit.ssh.apache" version="6.4.1" match="equivalent"/>
+      <import feature="org.eclipse.jgit" version="6.5.1" match="equivalent"/>
+      <import feature="org.eclipse.jgit.lfs" version="6.5.1" match="equivalent"/>
+      <import feature="org.eclipse.jgit.ssh.apache" version="6.5.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 979c357..ef1b0b8 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.4.1-SNAPSHOT</version>
+    <version>6.5.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 a091a7e..f45cb5a 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.4.1-SNAPSHOT</version>
+    <version>6.5.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 1b45654..7075f20 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.4.1.qualifier"
+      version="6.5.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.4.1" match="equivalent"/>
+      <import feature="org.eclipse.jgit" version="6.5.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 382375b..e49802b 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.4.1-SNAPSHOT</version>
+    <version>6.5.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.4.1-SNAPSHOT</version>
+      <version>6.5.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 c904e2d..c9514f4 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.4.1.qualifier"
+      version="6.5.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.4.1" match="equivalent"/>
+      <import feature="org.eclipse.jgit" version="6.5.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 669213d..60e984c 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.4.1-SNAPSHOT</version>
+    <version>6.5.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 8c4a14b..f39c18a 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.4.1.qualifier"
+      version="6.5.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.4.1" match="equivalent"/>
+      <import plugin="org.eclipse.jgit" version="6.5.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 7d1a08f..80e21c1 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.4.1-SNAPSHOT</version>
+    <version>6.5.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 ee07a32..8b87530 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.4.1.qualifier
+Bundle-Version: 6.5.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 d5ee87b..b4ad428 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,30 +1,26 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.17" sequenceNumber="1669195943">
+<target name="jgit-4.17" sequenceNumber="1677748416">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
+      <unit id="org.eclipse.jetty.http" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.io" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.security" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.server" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.servlet" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.util" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.util.ajax" version="10.0.13"/>
+      <repository id="jetty-10.0.x" location="https://download.eclipse.org/oomph/jetty/release/10.0.13/"/>
+    </location>
+    <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/"/>
+      <repository id="jetty-10.0.6" 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.9.1.v20220915-1632"/>
-      <unit id="com.google.gson.source" version="2.9.1.v20220915-1632"/>
+      <unit id="com.google.gson" version="2.10.1.v20230109-0753"/>
+      <unit id="com.google.gson.source" version="2.10.1.v20230109-0753"/>
       <unit id="com.jcraft.jsch" version="0.1.55.v20221112-0806"/>
       <unit id="com.jcraft.jsch.source" version="0.1.55.v20221112-0806"/>
       <unit id="com.jcraft.jzlib" version="1.1.3.v20220502-1820"/>
@@ -45,14 +41,14 @@
       <unit id="org.apache.ant.source" version="1.10.12.v20211102-1452"/>
       <unit id="org.apache.commons.codec" version="1.14.0.v20221112-0806"/>
       <unit id="org.apache.commons.codec.source" version="1.14.0.v20221112-0806"/>
-      <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.compress" version="1.22.0.v20221207-1049"/>
+      <unit id="org.apache.commons.compress.source" version="1.22.0.v20221207-1049"/>
       <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.v20221112-0806"/>
-      <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.13.v20221112-0806"/>
-      <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.httpcomponents.httpclient" version="4.5.14.v20221207-1049"/>
+      <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14.v20221207-1049"/>
+      <unit id="org.apache.httpcomponents.httpcore" version="4.4.16.v20221207-1049"/>
+      <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16.v20221207-1049"/>
       <unit id="org.apache.sshd.osgi" version="2.9.2.v20221117-1942"/>
       <unit id="org.apache.sshd.osgi.source" version="2.9.2.v20221117-1942"/>
       <unit id="org.apache.sshd.sftp" version="2.9.2.v20221117-1942"/>
@@ -87,7 +83,7 @@
       <unit id="org.slf4j.binding.simple.source" version="1.7.30.v20221112-0806"/>
       <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/R20221123021534/repository"/>
+      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20230302014618/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 60438d7..cf00c96 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/R20221123021534-2022-12.tpd"
+include "orbit/R20230302014618-2023-03.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 36e37b1..9ad44d8 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,30 +1,26 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.18" sequenceNumber="1669195943">
+<target name="jgit-4.18" sequenceNumber="1677748416">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
+      <unit id="org.eclipse.jetty.http" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.io" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.security" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.server" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.servlet" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.util" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.util.ajax" version="10.0.13"/>
+      <repository id="jetty-10.0.x" location="https://download.eclipse.org/oomph/jetty/release/10.0.13/"/>
+    </location>
+    <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/"/>
+      <repository id="jetty-10.0.6" 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.9.1.v20220915-1632"/>
-      <unit id="com.google.gson.source" version="2.9.1.v20220915-1632"/>
+      <unit id="com.google.gson" version="2.10.1.v20230109-0753"/>
+      <unit id="com.google.gson.source" version="2.10.1.v20230109-0753"/>
       <unit id="com.jcraft.jsch" version="0.1.55.v20221112-0806"/>
       <unit id="com.jcraft.jsch.source" version="0.1.55.v20221112-0806"/>
       <unit id="com.jcraft.jzlib" version="1.1.3.v20220502-1820"/>
@@ -45,14 +41,14 @@
       <unit id="org.apache.ant.source" version="1.10.12.v20211102-1452"/>
       <unit id="org.apache.commons.codec" version="1.14.0.v20221112-0806"/>
       <unit id="org.apache.commons.codec.source" version="1.14.0.v20221112-0806"/>
-      <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.compress" version="1.22.0.v20221207-1049"/>
+      <unit id="org.apache.commons.compress.source" version="1.22.0.v20221207-1049"/>
       <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.v20221112-0806"/>
-      <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.13.v20221112-0806"/>
-      <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.httpcomponents.httpclient" version="4.5.14.v20221207-1049"/>
+      <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14.v20221207-1049"/>
+      <unit id="org.apache.httpcomponents.httpcore" version="4.4.16.v20221207-1049"/>
+      <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16.v20221207-1049"/>
       <unit id="org.apache.sshd.osgi" version="2.9.2.v20221117-1942"/>
       <unit id="org.apache.sshd.osgi.source" version="2.9.2.v20221117-1942"/>
       <unit id="org.apache.sshd.sftp" version="2.9.2.v20221117-1942"/>
@@ -87,7 +83,7 @@
       <unit id="org.slf4j.binding.simple.source" version="1.7.30.v20221112-0806"/>
       <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/R20221123021534/repository"/>
+      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20230302014618/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 c894dfe..325b6de 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/R20221123021534-2022-12.tpd"
+include "orbit/R20230302014618-2023-03.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 2966e88..e570bf9 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,30 +1,26 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.19-staging" sequenceNumber="1669195941">
+<target name="jgit-4.19-staging" sequenceNumber="1677748416">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
+      <unit id="org.eclipse.jetty.http" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.io" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.security" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.server" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.servlet" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.util" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.util.ajax" version="10.0.13"/>
+      <repository id="jetty-10.0.x" location="https://download.eclipse.org/oomph/jetty/release/10.0.13/"/>
+    </location>
+    <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/"/>
+      <repository id="jetty-10.0.6" 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.9.1.v20220915-1632"/>
-      <unit id="com.google.gson.source" version="2.9.1.v20220915-1632"/>
+      <unit id="com.google.gson" version="2.10.1.v20230109-0753"/>
+      <unit id="com.google.gson.source" version="2.10.1.v20230109-0753"/>
       <unit id="com.jcraft.jsch" version="0.1.55.v20221112-0806"/>
       <unit id="com.jcraft.jsch.source" version="0.1.55.v20221112-0806"/>
       <unit id="com.jcraft.jzlib" version="1.1.3.v20220502-1820"/>
@@ -45,14 +41,14 @@
       <unit id="org.apache.ant.source" version="1.10.12.v20211102-1452"/>
       <unit id="org.apache.commons.codec" version="1.14.0.v20221112-0806"/>
       <unit id="org.apache.commons.codec.source" version="1.14.0.v20221112-0806"/>
-      <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.compress" version="1.22.0.v20221207-1049"/>
+      <unit id="org.apache.commons.compress.source" version="1.22.0.v20221207-1049"/>
       <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.v20221112-0806"/>
-      <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.13.v20221112-0806"/>
-      <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.httpcomponents.httpclient" version="4.5.14.v20221207-1049"/>
+      <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14.v20221207-1049"/>
+      <unit id="org.apache.httpcomponents.httpcore" version="4.4.16.v20221207-1049"/>
+      <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16.v20221207-1049"/>
       <unit id="org.apache.sshd.osgi" version="2.9.2.v20221117-1942"/>
       <unit id="org.apache.sshd.osgi.source" version="2.9.2.v20221117-1942"/>
       <unit id="org.apache.sshd.sftp" version="2.9.2.v20221117-1942"/>
@@ -87,7 +83,7 @@
       <unit id="org.slf4j.binding.simple.source" version="1.7.30.v20221112-0806"/>
       <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/R20221123021534/repository"/>
+      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20230302014618/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.19.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.tpd
index 6157842..08f3910 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,7 +1,7 @@
 target "jgit-4.19-staging" with source configurePhase
 
 include "projects/jetty-10.0.x.tpd"
-include "orbit/R20221123021534-2022-12.tpd"
+include "orbit/R20230302014618-2023-03.tpd"
 
 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 6e6e7fb..a3f7c2b 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,30 +1,26 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.20" sequenceNumber="1669195941">
+<target name="jgit-4.20" sequenceNumber="1677748416">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
+      <unit id="org.eclipse.jetty.http" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.io" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.security" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.server" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.servlet" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.util" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.util.ajax" version="10.0.13"/>
+      <repository id="jetty-10.0.x" location="https://download.eclipse.org/oomph/jetty/release/10.0.13/"/>
+    </location>
+    <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/"/>
+      <repository id="jetty-10.0.6" 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.9.1.v20220915-1632"/>
-      <unit id="com.google.gson.source" version="2.9.1.v20220915-1632"/>
+      <unit id="com.google.gson" version="2.10.1.v20230109-0753"/>
+      <unit id="com.google.gson.source" version="2.10.1.v20230109-0753"/>
       <unit id="com.jcraft.jsch" version="0.1.55.v20221112-0806"/>
       <unit id="com.jcraft.jsch.source" version="0.1.55.v20221112-0806"/>
       <unit id="com.jcraft.jzlib" version="1.1.3.v20220502-1820"/>
@@ -45,14 +41,14 @@
       <unit id="org.apache.ant.source" version="1.10.12.v20211102-1452"/>
       <unit id="org.apache.commons.codec" version="1.14.0.v20221112-0806"/>
       <unit id="org.apache.commons.codec.source" version="1.14.0.v20221112-0806"/>
-      <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.compress" version="1.22.0.v20221207-1049"/>
+      <unit id="org.apache.commons.compress.source" version="1.22.0.v20221207-1049"/>
       <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.v20221112-0806"/>
-      <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.13.v20221112-0806"/>
-      <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.httpcomponents.httpclient" version="4.5.14.v20221207-1049"/>
+      <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14.v20221207-1049"/>
+      <unit id="org.apache.httpcomponents.httpcore" version="4.4.16.v20221207-1049"/>
+      <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16.v20221207-1049"/>
       <unit id="org.apache.sshd.osgi" version="2.9.2.v20221117-1942"/>
       <unit id="org.apache.sshd.osgi.source" version="2.9.2.v20221117-1942"/>
       <unit id="org.apache.sshd.sftp" version="2.9.2.v20221117-1942"/>
@@ -87,7 +83,7 @@
       <unit id="org.slf4j.binding.simple.source" version="1.7.30.v20221112-0806"/>
       <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/R20221123021534/repository"/>
+      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20230302014618/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 afc5f34..07f8ab5 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/R20221123021534-2022-12.tpd"
+include "orbit/R20230302014618-2023-03.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 edfc027..908087f 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,30 +1,26 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.21" sequenceNumber="1669195941">
+<target name="jgit-4.21" sequenceNumber="1677748416">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
+      <unit id="org.eclipse.jetty.http" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.io" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.security" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.server" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.servlet" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.util" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.util.ajax" version="10.0.13"/>
+      <repository id="jetty-10.0.x" location="https://download.eclipse.org/oomph/jetty/release/10.0.13/"/>
+    </location>
+    <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/"/>
+      <repository id="jetty-10.0.6" 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.9.1.v20220915-1632"/>
-      <unit id="com.google.gson.source" version="2.9.1.v20220915-1632"/>
+      <unit id="com.google.gson" version="2.10.1.v20230109-0753"/>
+      <unit id="com.google.gson.source" version="2.10.1.v20230109-0753"/>
       <unit id="com.jcraft.jsch" version="0.1.55.v20221112-0806"/>
       <unit id="com.jcraft.jsch.source" version="0.1.55.v20221112-0806"/>
       <unit id="com.jcraft.jzlib" version="1.1.3.v20220502-1820"/>
@@ -45,14 +41,14 @@
       <unit id="org.apache.ant.source" version="1.10.12.v20211102-1452"/>
       <unit id="org.apache.commons.codec" version="1.14.0.v20221112-0806"/>
       <unit id="org.apache.commons.codec.source" version="1.14.0.v20221112-0806"/>
-      <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.compress" version="1.22.0.v20221207-1049"/>
+      <unit id="org.apache.commons.compress.source" version="1.22.0.v20221207-1049"/>
       <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.v20221112-0806"/>
-      <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.13.v20221112-0806"/>
-      <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.httpcomponents.httpclient" version="4.5.14.v20221207-1049"/>
+      <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14.v20221207-1049"/>
+      <unit id="org.apache.httpcomponents.httpcore" version="4.4.16.v20221207-1049"/>
+      <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16.v20221207-1049"/>
       <unit id="org.apache.sshd.osgi" version="2.9.2.v20221117-1942"/>
       <unit id="org.apache.sshd.osgi.source" version="2.9.2.v20221117-1942"/>
       <unit id="org.apache.sshd.sftp" version="2.9.2.v20221117-1942"/>
@@ -87,7 +83,7 @@
       <unit id="org.slf4j.binding.simple.source" version="1.7.30.v20221112-0806"/>
       <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/R20221123021534/repository"/>
+      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20230302014618/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 b701c81..c957b85 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/R20221123021534-2022-12.tpd"
+include "orbit/R20230302014618-2023-03.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 f5efb8c..b079284 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,30 +1,26 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.22" sequenceNumber="1669195941">
+<target name="jgit-4.22" sequenceNumber="1677748416">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
+      <unit id="org.eclipse.jetty.http" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.io" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.security" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.server" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.servlet" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.util" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.util.ajax" version="10.0.13"/>
+      <repository id="jetty-10.0.x" location="https://download.eclipse.org/oomph/jetty/release/10.0.13/"/>
+    </location>
+    <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/"/>
+      <repository id="jetty-10.0.6" 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.9.1.v20220915-1632"/>
-      <unit id="com.google.gson.source" version="2.9.1.v20220915-1632"/>
+      <unit id="com.google.gson" version="2.10.1.v20230109-0753"/>
+      <unit id="com.google.gson.source" version="2.10.1.v20230109-0753"/>
       <unit id="com.jcraft.jsch" version="0.1.55.v20221112-0806"/>
       <unit id="com.jcraft.jsch.source" version="0.1.55.v20221112-0806"/>
       <unit id="com.jcraft.jzlib" version="1.1.3.v20220502-1820"/>
@@ -45,14 +41,14 @@
       <unit id="org.apache.ant.source" version="1.10.12.v20211102-1452"/>
       <unit id="org.apache.commons.codec" version="1.14.0.v20221112-0806"/>
       <unit id="org.apache.commons.codec.source" version="1.14.0.v20221112-0806"/>
-      <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.compress" version="1.22.0.v20221207-1049"/>
+      <unit id="org.apache.commons.compress.source" version="1.22.0.v20221207-1049"/>
       <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.v20221112-0806"/>
-      <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.13.v20221112-0806"/>
-      <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.httpcomponents.httpclient" version="4.5.14.v20221207-1049"/>
+      <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14.v20221207-1049"/>
+      <unit id="org.apache.httpcomponents.httpcore" version="4.4.16.v20221207-1049"/>
+      <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16.v20221207-1049"/>
       <unit id="org.apache.sshd.osgi" version="2.9.2.v20221117-1942"/>
       <unit id="org.apache.sshd.osgi.source" version="2.9.2.v20221117-1942"/>
       <unit id="org.apache.sshd.sftp" version="2.9.2.v20221117-1942"/>
@@ -87,7 +83,7 @@
       <unit id="org.slf4j.binding.simple.source" version="1.7.30.v20221112-0806"/>
       <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/R20221123021534/repository"/>
+      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20230302014618/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 e69ab0a..61a16da 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/R20221123021534-2022-12.tpd"
+include "orbit/R20230302014618-2023-03.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 1100bf2..e85e43d 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,30 +1,26 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.23" sequenceNumber="1669195941">
+<target name="jgit-4.23" sequenceNumber="1677748416">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
+      <unit id="org.eclipse.jetty.http" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.io" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.security" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.server" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.servlet" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.util" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.util.ajax" version="10.0.13"/>
+      <repository id="jetty-10.0.x" location="https://download.eclipse.org/oomph/jetty/release/10.0.13/"/>
+    </location>
+    <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/"/>
+      <repository id="jetty-10.0.6" 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.9.1.v20220915-1632"/>
-      <unit id="com.google.gson.source" version="2.9.1.v20220915-1632"/>
+      <unit id="com.google.gson" version="2.10.1.v20230109-0753"/>
+      <unit id="com.google.gson.source" version="2.10.1.v20230109-0753"/>
       <unit id="com.jcraft.jsch" version="0.1.55.v20221112-0806"/>
       <unit id="com.jcraft.jsch.source" version="0.1.55.v20221112-0806"/>
       <unit id="com.jcraft.jzlib" version="1.1.3.v20220502-1820"/>
@@ -45,14 +41,14 @@
       <unit id="org.apache.ant.source" version="1.10.12.v20211102-1452"/>
       <unit id="org.apache.commons.codec" version="1.14.0.v20221112-0806"/>
       <unit id="org.apache.commons.codec.source" version="1.14.0.v20221112-0806"/>
-      <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.compress" version="1.22.0.v20221207-1049"/>
+      <unit id="org.apache.commons.compress.source" version="1.22.0.v20221207-1049"/>
       <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.v20221112-0806"/>
-      <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.13.v20221112-0806"/>
-      <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.httpcomponents.httpclient" version="4.5.14.v20221207-1049"/>
+      <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14.v20221207-1049"/>
+      <unit id="org.apache.httpcomponents.httpcore" version="4.4.16.v20221207-1049"/>
+      <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16.v20221207-1049"/>
       <unit id="org.apache.sshd.osgi" version="2.9.2.v20221117-1942"/>
       <unit id="org.apache.sshd.osgi.source" version="2.9.2.v20221117-1942"/>
       <unit id="org.apache.sshd.sftp" version="2.9.2.v20221117-1942"/>
@@ -87,7 +83,7 @@
       <unit id="org.slf4j.binding.simple.source" version="1.7.30.v20221112-0806"/>
       <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/R20221123021534/repository"/>
+      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20230302014618/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 87a7b7f..fde3bb6 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/R20221123021534-2022-12.tpd"
+include "orbit/R20230302014618-2023-03.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 ccd8d23..51d8fd5 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,30 +1,26 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.24" sequenceNumber="1669195941">
+<target name="jgit-4.24" sequenceNumber="1677748416">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
+      <unit id="org.eclipse.jetty.http" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.io" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.security" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.server" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.servlet" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.util" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.util.ajax" version="10.0.13"/>
+      <repository id="jetty-10.0.x" location="https://download.eclipse.org/oomph/jetty/release/10.0.13/"/>
+    </location>
+    <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/"/>
+      <repository id="jetty-10.0.6" 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.9.1.v20220915-1632"/>
-      <unit id="com.google.gson.source" version="2.9.1.v20220915-1632"/>
+      <unit id="com.google.gson" version="2.10.1.v20230109-0753"/>
+      <unit id="com.google.gson.source" version="2.10.1.v20230109-0753"/>
       <unit id="com.jcraft.jsch" version="0.1.55.v20221112-0806"/>
       <unit id="com.jcraft.jsch.source" version="0.1.55.v20221112-0806"/>
       <unit id="com.jcraft.jzlib" version="1.1.3.v20220502-1820"/>
@@ -45,14 +41,14 @@
       <unit id="org.apache.ant.source" version="1.10.12.v20211102-1452"/>
       <unit id="org.apache.commons.codec" version="1.14.0.v20221112-0806"/>
       <unit id="org.apache.commons.codec.source" version="1.14.0.v20221112-0806"/>
-      <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.compress" version="1.22.0.v20221207-1049"/>
+      <unit id="org.apache.commons.compress.source" version="1.22.0.v20221207-1049"/>
       <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.v20221112-0806"/>
-      <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.13.v20221112-0806"/>
-      <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.httpcomponents.httpclient" version="4.5.14.v20221207-1049"/>
+      <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14.v20221207-1049"/>
+      <unit id="org.apache.httpcomponents.httpcore" version="4.4.16.v20221207-1049"/>
+      <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16.v20221207-1049"/>
       <unit id="org.apache.sshd.osgi" version="2.9.2.v20221117-1942"/>
       <unit id="org.apache.sshd.osgi.source" version="2.9.2.v20221117-1942"/>
       <unit id="org.apache.sshd.sftp" version="2.9.2.v20221117-1942"/>
@@ -87,7 +83,7 @@
       <unit id="org.slf4j.binding.simple.source" version="1.7.30.v20221112-0806"/>
       <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/R20221123021534/repository"/>
+      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20230302014618/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.24.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.24.tpd
index 91108fc..6c480c3 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,7 +1,7 @@
 target "jgit-4.24" with source configurePhase
 
 include "projects/jetty-10.0.x.tpd"
-include "orbit/R20221123021534-2022-12.tpd"
+include "orbit/R20230302014618-2023-03.tpd"
 
 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
index 364f49e..b7260bf 100644
--- 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
@@ -1,30 +1,26 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.25" sequenceNumber="1669195941">
+<target name="jgit-4.25" sequenceNumber="1677748416">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
+      <unit id="org.eclipse.jetty.http" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.io" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.security" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.server" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.servlet" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.util" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.util.ajax" version="10.0.13"/>
+      <repository id="jetty-10.0.x" location="https://download.eclipse.org/oomph/jetty/release/10.0.13/"/>
+    </location>
+    <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/"/>
+      <repository id="jetty-10.0.6" 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.9.1.v20220915-1632"/>
-      <unit id="com.google.gson.source" version="2.9.1.v20220915-1632"/>
+      <unit id="com.google.gson" version="2.10.1.v20230109-0753"/>
+      <unit id="com.google.gson.source" version="2.10.1.v20230109-0753"/>
       <unit id="com.jcraft.jsch" version="0.1.55.v20221112-0806"/>
       <unit id="com.jcraft.jsch.source" version="0.1.55.v20221112-0806"/>
       <unit id="com.jcraft.jzlib" version="1.1.3.v20220502-1820"/>
@@ -45,14 +41,14 @@
       <unit id="org.apache.ant.source" version="1.10.12.v20211102-1452"/>
       <unit id="org.apache.commons.codec" version="1.14.0.v20221112-0806"/>
       <unit id="org.apache.commons.codec.source" version="1.14.0.v20221112-0806"/>
-      <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.compress" version="1.22.0.v20221207-1049"/>
+      <unit id="org.apache.commons.compress.source" version="1.22.0.v20221207-1049"/>
       <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.v20221112-0806"/>
-      <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.13.v20221112-0806"/>
-      <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.httpcomponents.httpclient" version="4.5.14.v20221207-1049"/>
+      <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14.v20221207-1049"/>
+      <unit id="org.apache.httpcomponents.httpcore" version="4.4.16.v20221207-1049"/>
+      <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16.v20221207-1049"/>
       <unit id="org.apache.sshd.osgi" version="2.9.2.v20221117-1942"/>
       <unit id="org.apache.sshd.osgi.source" version="2.9.2.v20221117-1942"/>
       <unit id="org.apache.sshd.sftp" version="2.9.2.v20221117-1942"/>
@@ -87,7 +83,7 @@
       <unit id="org.slf4j.binding.simple.source" version="1.7.30.v20221112-0806"/>
       <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/R20221123021534/repository"/>
+      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20230302014618/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.25.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.25.tpd
index 7a67a97..4542b13 100644
--- 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
@@ -1,7 +1,7 @@
 target "jgit-4.25" with source configurePhase
 
 include "projects/jetty-10.0.x.tpd"
-include "orbit/R20221123021534-2022-12.tpd"
+include "orbit/R20230302014618-2023-03.tpd"
 
 location "https://download.eclipse.org/releases/2022-09/" {
 	org.eclipse.osgi lazy
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.26.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.26.target
index 257f45e..57c9d0d 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.26.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.26.target
@@ -1,30 +1,26 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.25" sequenceNumber="1669195949">
+<target name="jgit-4.26" sequenceNumber="1677748097">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
+      <unit id="org.eclipse.jetty.http" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.io" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.security" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.server" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.servlet" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.util" version="10.0.13"/>
+      <unit id="org.eclipse.jetty.util.ajax" version="10.0.13"/>
+      <repository id="jetty-10.0.x" location="https://download.eclipse.org/oomph/jetty/release/10.0.13/"/>
+    </location>
+    <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/"/>
+      <repository id="jetty-10.0.6" 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.9.1.v20220915-1632"/>
-      <unit id="com.google.gson.source" version="2.9.1.v20220915-1632"/>
+      <unit id="com.google.gson" version="2.10.1.v20230109-0753"/>
+      <unit id="com.google.gson.source" version="2.10.1.v20230109-0753"/>
       <unit id="com.jcraft.jsch" version="0.1.55.v20221112-0806"/>
       <unit id="com.jcraft.jsch.source" version="0.1.55.v20221112-0806"/>
       <unit id="com.jcraft.jzlib" version="1.1.3.v20220502-1820"/>
@@ -45,14 +41,14 @@
       <unit id="org.apache.ant.source" version="1.10.12.v20211102-1452"/>
       <unit id="org.apache.commons.codec" version="1.14.0.v20221112-0806"/>
       <unit id="org.apache.commons.codec.source" version="1.14.0.v20221112-0806"/>
-      <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.compress" version="1.22.0.v20221207-1049"/>
+      <unit id="org.apache.commons.compress.source" version="1.22.0.v20221207-1049"/>
       <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.v20221112-0806"/>
-      <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.13.v20221112-0806"/>
-      <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.httpcomponents.httpclient" version="4.5.14.v20221207-1049"/>
+      <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14.v20221207-1049"/>
+      <unit id="org.apache.httpcomponents.httpcore" version="4.4.16.v20221207-1049"/>
+      <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16.v20221207-1049"/>
       <unit id="org.apache.sshd.osgi" version="2.9.2.v20221117-1942"/>
       <unit id="org.apache.sshd.osgi.source" version="2.9.2.v20221117-1942"/>
       <unit id="org.apache.sshd.sftp" version="2.9.2.v20221117-1942"/>
@@ -87,11 +83,11 @@
       <unit id="org.slf4j.binding.simple.source" version="1.7.30.v20221112-0806"/>
       <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/R20221123021534/repository"/>
+      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20230302014618/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-12/"/>
+      <repository location="https://download.eclipse.org/releases/2022-12/"/>
     </location>
   </locations>
 </target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.26.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.26.tpd
index 5fc49cd..25a223a 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.26.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.26.tpd
@@ -1,8 +1,8 @@
-target "jgit-4.25" with source configurePhase
+target "jgit-4.26" with source configurePhase
 
 include "projects/jetty-10.0.x.tpd"
-include "orbit/R20221123021534-2022-12.tpd"
+include "orbit/R20230302014618-2023-03.tpd"
 
-location "https://download.eclipse.org/staging/2022-12/" {
+location "https://download.eclipse.org/releases/2022-12/" {
 	org.eclipse.osgi lazy
 }
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20221123021534-2022-12.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20221123021534-2022-12.tpd
index bc4929b..378b848 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20221123021534-2022-12.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20221123021534-2022-12.tpd
@@ -1,4 +1,4 @@
-target "R20221123021534-2022-12" with source configurePhase
+target "S20230101190934" with source configurePhase
 // see https://download.eclipse.org/tools/orbit/downloads/
 
 location "https://download.eclipse.org/tools/orbit/downloads/drops/R20221123021534/repository" {
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20230302014618-2023-03.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20230302014618-2023-03.tpd
new file mode 100644
index 0000000..ad29378
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20230302014618-2023-03.tpd
@@ -0,0 +1,69 @@
+target "R20230302014618-2023-03" with source configurePhase
+// see https://download.eclipse.org/tools/orbit/downloads/
+
+location "https://download.eclipse.org/tools/orbit/downloads/drops/R20230302014618/repository" {
+	com.google.gson [2.10.1.v20230109-0753,2.10.1.v20230109-0753]
+	com.google.gson.source [2.10.1.v20230109-0753,2.10.1.v20230109-0753]
+	com.jcraft.jsch [0.1.55.v20221112-0806,0.1.55.v20221112-0806]
+	com.jcraft.jsch.source [0.1.55.v20221112-0806,0.1.55.v20221112-0806]
+	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.12.1.v20221103-2317,5.12.1.v20221103-2317]
+	com.sun.jna.source [5.12.1.v20221103-2317,5.12.1.v20221103-2317]
+	com.sun.jna.platform [5.12.1.v20221103-2317,5.12.1.v20221103-2317]
+	com.sun.jna.platform.source [5.12.1.v20221103-2317,5.12.1.v20221103-2317]
+	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.12.18.v20221114-2102,1.12.18.v20221114-2102]
+	net.bytebuddy.byte-buddy.source [1.12.18.v20221114-2102,1.12.18.v20221114-2102]
+	net.bytebuddy.byte-buddy-agent [1.12.18.v20221114-2102,1.12.18.v20221114-2102]
+	net.bytebuddy.byte-buddy-agent.source [1.12.18.v20221114-2102,1.12.18.v20221114-2102]
+	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.v20221112-0806,1.14.0.v20221112-0806]
+	org.apache.commons.codec.source [1.14.0.v20221112-0806,1.14.0.v20221112-0806]
+	org.apache.commons.compress [1.22.0.v20221207-1049,1.22.0.v20221207-1049]
+	org.apache.commons.compress.source [1.22.0.v20221207-1049,1.22.0.v20221207-1049]
+	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.14.v20221207-1049,4.5.14.v20221207-1049]
+	org.apache.httpcomponents.httpclient.source [4.5.14.v20221207-1049,4.5.14.v20221207-1049]
+	org.apache.httpcomponents.httpcore [4.4.16.v20221207-1049,4.4.16.v20221207-1049]
+	org.apache.httpcomponents.httpcore.source [4.4.16.v20221207-1049,4.4.16.v20221207-1049]
+	org.apache.sshd.osgi [2.9.2.v20221117-1942,2.9.2.v20221117-1942]
+	org.apache.sshd.osgi.source [2.9.2.v20221117-1942,2.9.2.v20221117-1942]
+	org.apache.sshd.sftp [2.9.2.v20221117-1942,2.9.2.v20221117-1942]
+	org.apache.sshd.sftp.source [2.9.2.v20221117-1942,2.9.2.v20221117-1942]
+	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.72.0.v20221013-1810,1.72.0.v20221013-1810]
+	org.bouncycastle.bcpg.source [1.72.0.v20221013-1810,1.72.0.v20221013-1810]
+	org.bouncycastle.bcpkix [1.72.0.v20221013-1810,1.72.0.v20221013-1810]
+	org.bouncycastle.bcpkix.source [1.72.0.v20221013-1810,1.72.0.v20221013-1810]
+	org.bouncycastle.bcprov [1.72.0.v20221013-1810,1.72.0.v20221013-1810]
+	org.bouncycastle.bcprov.source [1.72.0.v20221013-1810,1.72.0.v20221013-1810]
+	org.bouncycastle.bcutil [1.72.0.v20221013-1810,1.72.0.v20221013-1810]
+	org.bouncycastle.bcutil.source [1.72.0.v20221013-1810,1.72.0.v20221013-1810]
+	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.mockito-core [4.8.1.v20221103-2317,4.8.1.v20221103-2317]
+	org.mockito.mockito-core.source [4.8.1.v20221103-2317,4.8.1.v20221103-2317]
+	org.objenesis [3.3.0.v20221103-2317,3.3.0.v20221103-2317]
+	org.objenesis.source [3.3.0.v20221103-2317,3.3.0.v20221103-2317]
+	org.slf4j.api [1.7.30.v20221112-0806,1.7.30.v20221112-0806]
+	org.slf4j.api.source [1.7.30.v20221112-0806,1.7.30.v20221112-0806]
+	org.slf4j.binding.simple [1.7.30.v20221112-0806,1.7.30.v20221112-0806]
+	org.slf4j.binding.simple.source [1.7.30.v20221112-0806,1.7.30.v20221112-0806]
+	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 fbcb20c..8945542 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.4.1-SNAPSHOT</version>
+    <version>6.5.1-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.target</artifactId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/projects/jetty-10.0.x.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/projects/jetty-10.0.x.tpd
index 6c3ee18..e1afcff 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/projects/jetty-10.0.x.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/projects/jetty-10.0.x.tpd
@@ -1,20 +1,16 @@
 target "jetty-10.0.x" with source configurePhase
 
-location jetty-10.0.x "https://download.eclipse.org/eclipse/jetty/10.0.6/" {
+location jetty-10.0.x "https://download.eclipse.org/oomph/jetty/release/10.0.13/" {
+	org.eclipse.jetty.http [10.0.13,10.0.14]
+	org.eclipse.jetty.io [10.0.13,10.0.14]
+	org.eclipse.jetty.security [10.0.13,10.0.14]
+	org.eclipse.jetty.server [10.0.13,10.0.14]
+	org.eclipse.jetty.servlet [10.0.13,10.0.14]
+	org.eclipse.jetty.util [10.0.13,10.0.14]
+	org.eclipse.jetty.util.ajax [10.0.13,10.0.14]
+}
+
+location jetty-10.0.6 "https://download.eclipse.org/eclipse/jetty/10.0.6/" {
 	jakarta.servlet-api [4.0.0, 5.0.0)
 	jakarta.servlet-api.source [4.0.0, 5.0.0)
-	org.eclipse.jetty.http [10.0.6,10.0.6]
-	org.eclipse.jetty.http.source [10.0.6,10.0.6]
-	org.eclipse.jetty.io [10.0.6,10.0.6]
-	org.eclipse.jetty.io.source [10.0.6,10.0.6]
-	org.eclipse.jetty.security [10.0.6,10.0.6]
-	org.eclipse.jetty.security.source [10.0.6,10.0.6]
-	org.eclipse.jetty.server [10.0.6,10.0.6]
-	org.eclipse.jetty.server.source [10.0.6,10.0.6]
-	org.eclipse.jetty.servlet [10.0.6,10.0.6]
-	org.eclipse.jetty.servlet.source [10.0.6,10.0.6]
-	org.eclipse.jetty.util [10.0.6,10.0.6]
-	org.eclipse.jetty.util.source [10.0.6,10.0.6]
-	org.eclipse.jetty.util.ajax [10.0.6,10.0.6]
-	org.eclipse.jetty.util.ajax.source [10.0.6,10.0.6]
-}
+}
\ No newline at end of file
diff --git a/org.eclipse.jgit.packaging/pom.xml b/org.eclipse.jgit.packaging/pom.xml
index 3ccac7d..28ae7ce 100644
--- a/org.eclipse.jgit.packaging/pom.xml
+++ b/org.eclipse.jgit.packaging/pom.xml
@@ -16,7 +16,7 @@
 
   <groupId>org.eclipse.jgit</groupId>
   <artifactId>jgit.tycho.parent</artifactId>
-  <version>6.4.1-SNAPSHOT</version>
+  <version>6.5.1-SNAPSHOT</version>
   <packaging>pom</packaging>
 
   <name>JGit Tycho Parent</name>
@@ -24,7 +24,6 @@
   <properties>
     <java.version>11</java.version>
     <tycho-version>2.7.5</tycho-version>
-    <tycho-extras-version>${tycho-version}</tycho-extras-version>
     <target-platform>jgit-4.17</target-platform>
   </properties>
 
@@ -314,7 +313,7 @@
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-site-plugin</artifactId>
-          <version>3.9.1</version>
+          <version>3.12.1</version>
         </plugin>
       </plugins>
     </pluginManagement>
diff --git a/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF
index 0bdc408..a309b71 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.4.1.qualifier
+Bundle-Version: 6.5.1.qualifier
 Bundle-Vendor: %Bundle-Vendor
 Bundle-Localization: plugin
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: JavaSE-11
-Import-Package: org.eclipse.jgit.api;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.api.errors;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.diff;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.dircache;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.internal.diffmergetool;version="6.4.1",
- org.eclipse.jgit.internal.storage.file;version="6.4.1",
- org.eclipse.jgit.junit;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.lib;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.lib.internal;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.merge;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.pgm;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.pgm.internal;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.pgm.opt;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.revwalk;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.storage.file;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.transport;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.treewalk;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.util;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.util.io;version="[6.4.1,6.5.0)",
+Import-Package: org.eclipse.jgit.api;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.api.errors;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.diff;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.dircache;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.internal.diffmergetool;version="6.5.1",
+ org.eclipse.jgit.internal.storage.file;version="6.5.1",
+ org.eclipse.jgit.junit;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.lib;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.lib.internal;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.merge;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.pgm;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.pgm.internal;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.pgm.opt;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.revwalk;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.storage.file;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.transport;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.treewalk;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.util;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.util.io;version="[6.5.1,6.6.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 dad9288..86d6cc9 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.4.1-SNAPSHOT</version>
+    <version>6.5.1-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.pgm.test</artifactId>
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CloneTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CloneTest.java
index 4cbd61c..cbb5bbb 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CloneTest.java
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CloneTest.java
@@ -17,14 +17,20 @@
 import static org.junit.Assert.assertTrue;
 
 import java.io.File;
+import java.time.Instant;
 import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
 
 import org.eclipse.jgit.api.Git;
 import org.eclipse.jgit.junit.JGitTestUtil;
 import org.eclipse.jgit.junit.MockSystemReader;
+import org.eclipse.jgit.junit.TestRepository;
 import org.eclipse.jgit.lib.CLIRepositoryTestCase;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.PersonIdent;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.RefUpdate;
 import org.eclipse.jgit.lib.Repository;
@@ -41,10 +47,14 @@
 
 	private Git git;
 
+	private TestRepository<Repository> tr;
+
 	@Override
 	@Before
 	public void setUp() throws Exception {
 		super.setUp();
+
+		tr = new TestRepository<>(db);
 		git = new Git(db);
 	}
 
@@ -112,6 +122,22 @@
 		return git.commit().setMessage("Initial commit").call();
 	}
 
+	private RevCommit createSecondCommit() throws Exception {
+		JGitTestUtil.writeTrashFile(db, "Test.txt", "Some change");
+		git.add().addFilepattern("Test.txt").call();
+		return git.commit()
+				.setCommitter(new PersonIdent(this.committer, tr.getDate()))
+				.setMessage("Second commit").call();
+	}
+
+	private RevCommit createThirdCommit() throws Exception {
+		JGitTestUtil.writeTrashFile(db, "change.txt", "another change");
+		git.add().addFilepattern("change.txt").call();
+		return git.commit()
+				.setCommitter(new PersonIdent(this.committer, tr.getDate()))
+				.setMessage("Third commit").call();
+	}
+
 	@Test
 	public void testCloneEmpty() throws Exception {
 		File gitDir = db.getDirectory();
@@ -203,4 +229,117 @@
 		assertEquals("refs/*", fetchRefSpec.getDestination());
 		assertNotNull(git2.getRepository().exactRef("refs/meta/foo/bar"));
 	}
+
+	@Test
+	public void testDepth() throws Exception {
+		createInitialCommit();
+		createSecondCommit();
+		createThirdCommit();
+
+		File gitDir = db.getDirectory();
+		String sourceURI = gitDir.toURI().toString();
+		File target = createTempDirectory("target");
+		String cmd = "git clone --depth 1 " + sourceURI + " "
+				+ shellQuote(target.getPath());
+		String[] result = execute(cmd);
+		assertArrayEquals(new String[] {
+				"Cloning into '" + target.getPath() + "'...", "", "" }, result);
+
+		Git git2 = Git.open(target);
+		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());
+	}
+
+	@Test
+	public void testDepth2() throws Exception {
+		createInitialCommit();
+		createSecondCommit();
+		createThirdCommit();
+
+		File gitDir = db.getDirectory();
+		String sourceURI = gitDir.toURI().toString();
+		File target = createTempDirectory("target");
+		String cmd = "git clone --depth 2 " + sourceURI + " "
+				+ shellQuote(target.getPath());
+		String[] result = execute(cmd);
+		assertArrayEquals(new String[] {
+				"Cloning into '" + target.getPath() + "'...", "", "" }, result);
+
+		Git git2 = Git.open(target);
+		addRepoToClose(git2.getRepository());
+
+		List<RevCommit> log = StreamSupport
+				.stream(git2.log().all().call().spliterator(), false)
+				.collect(Collectors.toList());
+		assertEquals(2, log.size());
+		assertEquals(List.of("Third commit", "Second commit"), log.stream()
+				.map(RevCommit::getFullMessage).collect(Collectors.toList()));
+	}
+
+	@Test
+	public void testCloneRepositoryWithShallowSince() throws Exception {
+		createInitialCommit();
+		tr.tick(30);
+		RevCommit secondCommit = createSecondCommit();
+		tr.tick(45);
+		createThirdCommit();
+
+		File gitDir = db.getDirectory();
+		String sourceURI = gitDir.toURI().toString();
+		File target = createTempDirectory("target");
+		String cmd = "git clone --shallow-since="
+				+ Instant.ofEpochSecond(secondCommit.getCommitTime()).toString()
+				+ " " + sourceURI + " " + shellQuote(target.getPath());
+		String[] result = execute(cmd);
+		assertArrayEquals(new String[] {
+				"Cloning into '" + target.getPath() + "'...", "", "" }, result);
+
+		Git git2 = Git.open(target);
+		addRepoToClose(git2.getRepository());
+
+		List<RevCommit> log = StreamSupport
+				.stream(git2.log().all().call().spliterator(), false)
+				.collect(Collectors.toList());
+		assertEquals(2, log.size());
+		assertEquals(List.of("Third commit", "Second commit"), log.stream()
+				.map(RevCommit::getFullMessage).collect(Collectors.toList()));
+	}
+
+	@Test
+	public void testCloneRepositoryWithShallowExclude() throws Exception {
+		final RevCommit firstCommit = createInitialCommit();
+		final RevCommit secondCommit = createSecondCommit();
+		createThirdCommit();
+
+		File gitDir = db.getDirectory();
+		String sourceURI = gitDir.toURI().toString();
+		File target = createTempDirectory("target");
+		String cmd = "git clone --shallow-exclude="
+				+ firstCommit.getId().getName() + " --shallow-exclude="
+				+ secondCommit.getId().getName() + " " + sourceURI + " "
+				+ shellQuote(target.getPath());
+		String[] result = execute(cmd);
+		assertArrayEquals(new String[] {
+				"Cloning into '" + target.getPath() + "'...", "", "" }, result);
+
+		Git git2 = Git.open(target);
+		addRepoToClose(git2.getRepository());
+
+		List<RevCommit> log = StreamSupport
+				.stream(git2.log().all().call().spliterator(), false)
+				.collect(Collectors.toList());
+		assertEquals(1, log.size());
+		assertEquals(List.of("Third commit"), log.stream()
+				.map(RevCommit::getFullMessage).collect(Collectors.toList()));
+	}
+
 }
diff --git a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
index ecbb24c..5399d7d 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.4.1.qualifier
+Bundle-Version: 6.5.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.4.1,6.5.0)",
- org.eclipse.jgit.api.errors;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.archive;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.awtui;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.blame;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.diff;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.dircache;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.errors;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.gitrepo;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.internal.storage.file;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.internal.diffmergetool;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.internal.storage.io;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.internal.storage.pack;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.internal.storage.reftable;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.lfs;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.lfs.server;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.lfs.server.fs;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.lfs.server.s3;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.lib;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.merge;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.lib.internal;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.nls;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.notes;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.revplot;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.revwalk;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.revwalk.filter;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.storage.file;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.storage.pack;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.transport;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.transport.http.apache;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.transport.resolver;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.transport.ssh.jsch;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.transport.sshd;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.treewalk;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.treewalk.filter;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.util;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.util.io;version="[6.4.1,6.5.0)",
+ org.eclipse.jgit.api;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.api.errors;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.archive;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.awtui;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.blame;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.diff;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.dircache;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.errors;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.gitrepo;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.internal.storage.file;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.internal.diffmergetool;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.internal.storage.io;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.internal.storage.reftable;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.lfs;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.lfs.server;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.lfs.server.fs;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.lfs.server.s3;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.lib;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.merge;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.lib.internal;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.nls;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.notes;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.revplot;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.revwalk;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.revwalk.filter;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.storage.file;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.storage.pack;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.transport;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.transport.http.apache;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.transport.resolver;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.transport.ssh.jsch;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.transport.sshd;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.treewalk;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.treewalk.filter;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.util;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.util.io;version="[6.5.1,6.6.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.4.1";
+Export-Package: org.eclipse.jgit.console;version="6.5.1";
  uses:="org.eclipse.jgit.transport,
   org.eclipse.jgit.util",
- org.eclipse.jgit.pgm;version="6.4.1";
+ org.eclipse.jgit.pgm;version="6.5.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.4.1";
+ org.eclipse.jgit.pgm.debug;version="6.5.1";
   uses:="org.eclipse.jgit.util.io,
    org.eclipse.jgit.pgm,
    org.eclipse.jetty.servlet",
- org.eclipse.jgit.pgm.internal;version="6.4.1";
+ org.eclipse.jgit.pgm.internal;version="6.5.1";
   x-friends:="org.eclipse.jgit.pgm.test,
    org.eclipse.jgit.test",
- org.eclipse.jgit.pgm.opt;version="6.4.1";
+ org.eclipse.jgit.pgm.opt;version="6.5.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 e2bc4f0..2241425 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.4.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="6.4.1.qualifier";roots="."
+Bundle-Version: 6.5.1.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="6.5.1.qualifier";roots="."
diff --git a/org.eclipse.jgit.pgm/pom.xml b/org.eclipse.jgit.pgm/pom.xml
index eda51f1..a45c2b1 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.4.1-SNAPSHOT</version>
+    <version>6.5.1-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.pgm</artifactId>
diff --git a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties
index e0db759..15fe096 100644
--- a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties
+++ b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties
@@ -137,6 +137,7 @@
 metaVar_commitPaths=paths
 metaVar_configFile=FILE
 metaVar_connProp=conn.prop
+metaVar_depth=<depth>
 metaVar_diffAlg=ALGORITHM
 metaVar_directory=DIRECTORY
 metaVar_extraArgument=ours|theirs
@@ -144,6 +145,7 @@
 metaVar_filepattern=filepattern
 metaVar_gitDir=GIT_DIR
 metaVar_hostName=HOSTNAME
+metaVar_instant=<instant>
 metaVar_lfsStorage=STORAGE
 metaVar_linesOfContext=lines
 metaVar_message=message
@@ -168,6 +170,8 @@
 metaVar_s3StorageClass=STORAGE-CLASS
 metaVar_seconds=SECONDS
 metaVar_service=SERVICE
+metaVar_shallowExclude=<revision>
+metaVar_shallowSince=<date>
 metaVar_tagLocalUser=<GPG key ID>
 metaVar_tool=TOOL
 metaVar_treeish=tree-ish
@@ -375,6 +379,7 @@
 usage_diffAlgorithm=the diff algorithm to use. Currently supported are: 'myers', 'histogram'
 usage_DiffTool=git difftool is a Git command that allows you to compare and edit files between revisions using common diff tools.\ngit difftool is a frontend to git diff and accepts the same options and arguments.
 usage_MergeTool=git-mergetool - Run merge conflict resolution tools to resolve merge conflicts.\nUse git mergetool to run one of several merge utilities to resolve merge conflicts. It is typically run after git merge.
+usage_depth=Limit fetching to the specified number of commits from the tip of each remote branch history.
 usage_directoriesToExport=directories to export
 usage_disableTheServiceInAllRepositories=disable the service in all repositories
 usage_displayAListOfAllRegisteredJgitCommands=Display a list of all registered jgit commands
@@ -448,6 +453,8 @@
 usage_runLfsStore=Run LFS Store in a given directory
 usage_S3NoSslVerify=Skip verification of Amazon server certificate and hostname
 usage_setTheGitRepositoryToOperateOn=set the git repository to operate on
+usage_shallowExclude=Deepen or shorten the history of a shallow repository to exclude commits reachable from a specified remote branch or tag. 
+usage_shallowSince=Deepen or shorten the history of a shallow repository to include all reachable commits after <date>.
 usage_show=Display one commit
 usage_showRefNamesMatchingCommits=Show ref names matching commits
 usage_showPatch=display patch
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Clone.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Clone.java
index f28915d..9f9fa8f 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Clone.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Clone.java
@@ -13,7 +13,10 @@
 import java.io.File;
 import java.io.IOException;
 import java.text.MessageFormat;
+import java.time.Instant;
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.List;
 
 import org.eclipse.jgit.api.CloneCommand;
 import org.eclipse.jgit.api.Git;
@@ -48,6 +51,15 @@
 	@Option(name = "--quiet", usage = "usage_quiet")
 	private Boolean quiet;
 
+	@Option(name = "--depth", metaVar = "metaVar_depth", usage = "usage_depth")
+	private Integer depth = null;
+
+	@Option(name = "--shallow-since", metaVar = "metaVar_shallowSince", usage = "usage_shallowSince")
+	private Instant shallowSince = null;
+
+	@Option(name = "--shallow-exclude", metaVar = "metaVar_shallowExclude", usage = "usage_shallowExclude")
+	private List<String> shallowExcludes = new ArrayList<>();
+
 	@Option(name = "--recurse-submodules", usage = "usage_recurseSubmodules")
 	private boolean cloneSubmodules;
 
@@ -97,6 +109,16 @@
 				.setMirror(isMirror).setNoCheckout(noCheckout).setBranch(branch)
 				.setCloneSubmodules(cloneSubmodules).setTimeout(timeout);
 
+		if (depth != null) {
+			command.setDepth(depth.intValue());
+		}
+		if (shallowSince != null) {
+			command.setShallowSince(shallowSince);
+		}
+		for (String shallowExclude : shallowExcludes) {
+			command.addShallowExclude(shallowExclude);
+		}
+
 		command.setGitDir(gitdir == null ? null : new File(gitdir));
 		command.setDirectory(localNameF);
 		boolean msgs = quiet == null || !quiet.booleanValue();
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Fetch.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Fetch.java
index fbce4a5..2e0c36b 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Fetch.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Fetch.java
@@ -14,6 +14,8 @@
 
 import java.io.IOException;
 import java.text.MessageFormat;
+import java.time.Instant;
+import java.util.ArrayList;
 import java.util.List;
 
 import org.eclipse.jgit.api.FetchCommand;
@@ -62,6 +64,15 @@
 	@Option(name = "--tags", usage="usage_tags", aliases = { "-t" })
 	private Boolean tags;
 
+	@Option(name = "--depth", metaVar = "metaVar_depth", usage = "usage_depth")
+	private Integer depth = null;
+
+	@Option(name = "--shallow-since", metaVar = "metaVar_shallowSince", usage = "usage_shallowSince")
+	private Instant shallowSince = null;
+
+	@Option(name = "--shallow-exclude", metaVar = "metaVar_shallowExclude", usage = "usage_shallowExclude")
+	private List<String> shallowExcludes = new ArrayList<>();
+
 	@Option(name = "--no-tags", usage = "usage_notags", aliases = { "-n" })
 	void notags(@SuppressWarnings("unused")
 	final boolean ignored) {
@@ -120,6 +131,15 @@
 				fetch.setTagOpt(tags.booleanValue() ? TagOpt.FETCH_TAGS
 						: TagOpt.NO_TAGS);
 			}
+			if (depth != null) {
+				fetch.setDepth(depth.intValue());
+			}
+			if (shallowSince != null) {
+				fetch.setShallowSince(shallowSince);
+			}
+			for (String shallowExclude : shallowExcludes) {
+				fetch.addShallowExclude(shallowExclude);
+			}
 			if (0 <= timeout) {
 				fetch.setTimeout(timeout);
 			}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java
index 490f800..d07268b 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java
@@ -214,6 +214,7 @@
 	/***/ public String metaVar_filepattern;
 	/***/ public String metaVar_gitDir;
 	/***/ public String metaVar_hostName;
+	/***/ public String metaVar_instant;
 	/***/ public String metaVar_lfsStorage;
 	/***/ public String metaVar_linesOfContext;
 	/***/ public String metaVar_message;
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/CmdLineParser.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/CmdLineParser.java
index 5d32e65..df0b39b 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/CmdLineParser.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/CmdLineParser.java
@@ -13,6 +13,7 @@
 import java.io.IOException;
 import java.io.Writer;
 import java.lang.reflect.Field;
+import java.time.Instant;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
@@ -55,6 +56,7 @@
 		registry.registerHandler(RevCommit.class, RevCommitHandler.class);
 		registry.registerHandler(RevTree.class, RevTreeHandler.class);
 		registry.registerHandler(List.class, OptionWithValuesListHandler.class);
+		registry.registerHandler(Instant.class, InstantHandler.class);
 	}
 
 	private final Repository db;
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/InstantHandler.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/InstantHandler.java
new file mode 100644
index 0000000..feee78e
--- /dev/null
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/InstantHandler.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2022, Harald Weiner 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.pgm.opt;
+
+import java.time.Instant;
+
+import org.eclipse.jgit.pgm.internal.CLIText;
+import org.kohsuke.args4j.CmdLineException;
+import org.kohsuke.args4j.CmdLineParser;
+import org.kohsuke.args4j.OptionDef;
+import org.kohsuke.args4j.spi.OptionHandler;
+import org.kohsuke.args4j.spi.Parameters;
+import org.kohsuke.args4j.spi.Setter;
+
+/**
+ * Custom argument handler {@link java.time.Instant} from string values.
+ * <p>
+ * Assumes the parser has been initialized with a Repository.
+ *
+ * @since 6.5
+ */
+public class InstantHandler extends OptionHandler<Instant> {
+	/**
+	 * Create a new handler for the command name.
+	 * <p>
+	 * This constructor is used only by args4j.
+	 *
+	 * @param parser
+	 *            a {@link org.kohsuke.args4j.CmdLineParser} object.
+	 * @param option
+	 *            a {@link org.kohsuke.args4j.OptionDef} object.
+	 * @param setter
+	 *            a {@link org.kohsuke.args4j.spi.Setter} object.
+	 */
+	public InstantHandler(CmdLineParser parser, OptionDef option,
+			Setter<? super Instant> setter) {
+		super(parser, option, setter);
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public int parseArguments(Parameters params) throws CmdLineException {
+		Instant instant = Instant.parse(params.getParameter(0));
+		setter.addValue(instant);
+		return 1;
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public String getDefaultMetaVariable() {
+		return CLIText.get().metaVar_instant;
+	}
+}
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 0f26a38..cce6486 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.4.1.qualifier
+Bundle-Version: 6.5.1.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %Bundle-Vendor
-Fragment-Host: org.eclipse.jgit.ssh.apache;bundle-version="[6.4.1,6.5.0)"
+Fragment-Host: org.eclipse.jgit.ssh.apache;bundle-version="[6.5.1,6.6.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.4.1,6.5.0)",
- org.eclipse.jgit.nls;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.util;version="[6.4.1,6.5.0)"
+Import-Package: org.eclipse.jgit.transport.sshd;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.nls;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.util;version="[6.5.1,6.6.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.4.1";x-internal:=true
+Export-Package: org.eclipse.jgit.internal.transport.sshd.agent.connector;version="6.5.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 f59f6fe..3039554 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.4.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.ssh.apache.agent;version="6.4.1.qualifier";roots="."
+Bundle-Version: 6.5.1.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.ssh.apache.agent;version="6.5.1.qualifier";roots="."
diff --git a/org.eclipse.jgit.ssh.apache.agent/pom.xml b/org.eclipse.jgit.ssh.apache.agent/pom.xml
index 9f16d47..200d5b7 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.4.1-SNAPSHOT</version>
+    <version>6.5.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 3175a4a..22d9576 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.4.1.qualifier
+Bundle-Version: 6.5.1.qualifier
 Bundle-Vendor: %Bundle-Vendor
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-11
@@ -21,16 +21,16 @@
  org.apache.sshd.core;version="[2.9.2,2.10.0)",
  org.apache.sshd.server;version="[2.9.2,2.10.0)",
  org.apache.sshd.server.forward;version="[2.9.2,2.10.0)",
- org.eclipse.jgit.api;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.api.errors;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.internal.transport.sshd.proxy;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.junit;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.junit.ssh;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.lib;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.transport;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.transport.sshd;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.transport.sshd.agent;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.util;version="[6.4.1,6.5.0)",
+ org.eclipse.jgit.api;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.api.errors;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.internal.transport.sshd.proxy;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.junit;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.junit.ssh;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.lib;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.transport;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.transport.sshd;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.transport.sshd.agent;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.util;version="[6.5.1,6.6.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 6adcb89..ec553f3 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.4.1-SNAPSHOT</version>
+    <version>6.5.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 786c664..a44ff22 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.4.1.qualifier
+Bundle-Version: 6.5.1.qualifier
 Bundle-RequiredExecutionEnvironment: JavaSE-11
-Export-Package: org.eclipse.jgit.internal.transport.sshd;version="6.4.1";x-internal:=true;
+Export-Package: org.eclipse.jgit.internal.transport.sshd;version="6.5.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.4.1";x-internal:=true,
- org.eclipse.jgit.internal.transport.sshd.auth;version="6.4.1";x-internal:=true,
- org.eclipse.jgit.internal.transport.sshd.proxy;version="6.4.1";x-friends:="org.eclipse.jgit.ssh.apache.test",
- org.eclipse.jgit.transport.sshd;version="6.4.1";
+ org.eclipse.jgit.internal.transport.sshd.agent;version="6.5.1";x-internal:=true,
+ org.eclipse.jgit.internal.transport.sshd.auth;version="6.5.1";x-internal:=true,
+ org.eclipse.jgit.internal.transport.sshd.proxy;version="6.5.1";x-friends:="org.eclipse.jgit.ssh.apache.test",
+ org.eclipse.jgit.transport.sshd;version="6.5.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.4.1"
+ org.eclipse.jgit.transport.sshd.agent;version="6.5.1"
 Import-Package: net.i2p.crypto.eddsa;version="[0.3.0,0.4.0)",
  org.apache.sshd.agent;version="[2.9.2,2.10.0)",
  org.apache.sshd.client;version="[2.9.2,2.10.0)",
@@ -86,12 +86,12 @@
  org.apache.sshd.sftp;version="[2.9.2,2.10.0)",
  org.apache.sshd.sftp.client;version="[2.9.2,2.10.0)",
  org.apache.sshd.sftp.common;version="[2.9.2,2.10.0)",
- org.eclipse.jgit.annotations;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.errors;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.fnmatch;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.internal.storage.file;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.internal.transport.ssh;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.nls;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.transport;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.util;version="[6.4.1,6.5.0)",
+ org.eclipse.jgit.annotations;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.errors;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.fnmatch;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.internal.storage.file;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.internal.transport.ssh;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.nls;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.transport;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.util;version="[6.5.1,6.6.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 2693c45..7d5e247 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.4.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.ssh.apache;version="6.4.1.qualifier";roots="."
+Bundle-Version: 6.5.1.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.ssh.apache;version="6.5.1.qualifier";roots="."
diff --git a/org.eclipse.jgit.ssh.apache/pom.xml b/org.eclipse.jgit.ssh.apache/pom.xml
index e68e68a..88557a9 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.4.1-SNAPSHOT</version>
+    <version>6.5.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/proxy/AbstractClientProxyConnector.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/AbstractClientProxyConnector.java
index a8e33af..a05d641 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/AbstractClientProxyConnector.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/AbstractClientProxyConnector.java
@@ -27,8 +27,8 @@
 public abstract class AbstractClientProxyConnector
 		implements StatefulProxyConnector {
 
-	private static final long DEFAULT_PROXY_TIMEOUT_MILLIS = TimeUnit.SECONDS
-			.toMillis(30L);
+	private static final long DEFAULT_PROXY_TIMEOUT_NANOS = TimeUnit.SECONDS
+			.toNanos(30L);
 
 	/** Guards {@link #done} and {@link #bufferedCommands}. */
 	private final Object lock = new Object();
@@ -39,7 +39,7 @@
 
 	private AtomicReference<Runnable> unregister = new AtomicReference<>();
 
-	private long remainingProxyProtocolTime = DEFAULT_PROXY_TIMEOUT_MILLIS;
+	private long remainingProxyProtocolTime = DEFAULT_PROXY_TIMEOUT_NANOS;
 
 	private long lastProxyOperationTime = 0L;
 
@@ -85,12 +85,12 @@
 	 *            to initialize for
 	 */
 	protected void init(ClientSession session) {
-		remainingProxyProtocolTime = session.getLongProperty(
+		long millis = session.getLongProperty(
 				StatefulProxyConnector.TIMEOUT_PROPERTY,
-				DEFAULT_PROXY_TIMEOUT_MILLIS);
-		if (remainingProxyProtocolTime <= 0L) {
-			remainingProxyProtocolTime = DEFAULT_PROXY_TIMEOUT_MILLIS;
-		}
+				0);
+		remainingProxyProtocolTime = (millis > 0)
+				? TimeUnit.MILLISECONDS.toNanos(millis)
+				: DEFAULT_PROXY_TIMEOUT_NANOS;
 		if (session instanceof JGitClientSession) {
 			JGitClientSession s = (JGitClientSession) session;
 			unregister.set(() -> s.setProxyHandler(null));
@@ -115,12 +115,9 @@
 		if (last != 0L) {
 			long elapsed = now - last;
 			remaining -= elapsed;
-			if (remaining < 0L) {
-				remaining = 10L; // Give it grace period.
-			}
+			remainingProxyProtocolTime = remaining;
 		}
-		remainingProxyProtocolTime = remaining;
-		return remaining;
+		return Math.max(remaining / 1_000_000L, 10L); // Give it grace period.
 	}
 
 	/**
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 106cf13..2e57de4 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.4.1.qualifier
+Bundle-Version: 6.5.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.4.1,6.5.0)",
- org.eclipse.jgit.junit;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.junit.ssh;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.lib;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.transport;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.transport.ssh.jsch;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.util;version="[6.4.1,6.5.0)",
+ org.eclipse.jgit.errors;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.junit;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.junit.ssh;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.lib;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.transport;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.transport.ssh.jsch;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.util;version="[6.5.1,6.6.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 8984813..158e967 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.4.1-SNAPSHOT</version>
+    <version>6.5.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 785b873..61a4ee1 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.4.1,6.5.0)"
+Fragment-Host: org.eclipse.jgit;bundle-version="[6.5.1,6.6.0)"
 Bundle-Vendor: %Bundle-Vendor
 Bundle-Localization: plugin
 Bundle-ActivationPolicy: lazy
-Bundle-Version: 6.4.1.qualifier
+Bundle-Version: 6.5.1.qualifier
 Bundle-RequiredExecutionEnvironment: JavaSE-11
-Export-Package: org.eclipse.jgit.transport.ssh.jsch;version="6.4.1"
+Export-Package: org.eclipse.jgit.transport.ssh.jsch;version="6.5.1"
 Import-Package: com.jcraft.jsch;version="[0.1.37,0.2.0)",
- org.eclipse.jgit.errors;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.internal;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.internal.transport.ssh;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.nls;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.transport;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.util;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.util.io;version="[6.4.1,6.5.0)",
+ org.eclipse.jgit.errors;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.internal;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.internal.transport.ssh;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.nls;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.transport;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.util;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.util.io;version="[6.5.1,6.6.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 37aaf8d..f96290c 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.4.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.ssh.jsch;version="6.4.1.qualifier";roots="."
+Bundle-Version: 6.5.1.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.ssh.jsch;version="6.5.1.qualifier";roots="."
diff --git a/org.eclipse.jgit.ssh.jsch/pom.xml b/org.eclipse.jgit.ssh.jsch/pom.xml
index f859caf..9336ce1 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.4.1-SNAPSHOT</version>
+    <version>6.5.1-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.ssh.jsch</artifactId>
diff --git a/org.eclipse.jgit.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.test/META-INF/MANIFEST.MF
index a68d5e3..e2390b3 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.4.1.qualifier
+Bundle-Version: 6.5.1.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %Bundle-Vendor
 Bundle-RequiredExecutionEnvironment: JavaSE-11
@@ -16,62 +16,64 @@
  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.4.1,6.5.0)",
- org.eclipse.jgit.api;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.api.errors;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.archive;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.attributes;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.awtui;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.blame;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.diff;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.dircache;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.errors;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.events;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.fnmatch;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.gitrepo;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.hooks;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.ignore;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.ignore.internal;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.internal;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.internal.diff;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.internal.diffmergetool;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.internal.fsck;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.internal.revwalk;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.internal.storage.dfs;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.internal.storage.file;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.internal.storage.io;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.internal.storage.pack;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.internal.storage.reftable;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.internal.transport.connectivity;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.internal.transport.http;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.internal.transport.parser;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.internal.transport.ssh;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.junit;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.junit.time;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.lfs;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.lib;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.lib.internal;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.logging;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.merge;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.nls;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.notes;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.patch;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.pgm;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.pgm.internal;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.revplot;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.revwalk;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.revwalk.filter;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.storage.file;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.storage.pack;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.submodule;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.transport;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.transport.http;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.transport.resolver;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.treewalk;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.treewalk.filter;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.util;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.util.io;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.util.sha1;version="[6.4.1,6.5.0)",
+ org.eclipse.jgit.annotations;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.api;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.api.errors;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.archive;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.attributes;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.awtui;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.blame;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.diff;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.dircache;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.errors;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.events;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.fnmatch;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.gitrepo;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.hooks;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.ignore;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.ignore.internal;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.internal;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.internal.diff;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.internal.diffmergetool;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.internal.fsck;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.internal.revwalk;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.internal.storage.commitgraph;version="6.5.1",
+ org.eclipse.jgit.internal.storage.dfs;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.internal.storage.file;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.internal.storage.io;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.internal.storage.memory;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.internal.storage.reftable;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.internal.transport.connectivity;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.internal.transport.http;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.internal.transport.parser;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.internal.transport.ssh;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.junit;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.junit.time;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.lfs;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.lib;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.lib.internal;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.logging;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.merge;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.nls;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.notes;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.patch;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.pgm;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.pgm.internal;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.revplot;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.revwalk;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.revwalk.filter;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.storage.file;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.storage.pack;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.submodule;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.transport;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.transport.http;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.transport.resolver;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.treewalk;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.treewalk.filter;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.util;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.util.io;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.util.sha1;version="[6.5.1,6.6.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)",
diff --git a/org.eclipse.jgit.test/pom.xml b/org.eclipse.jgit.test/pom.xml
index 0274fa9..b7d932a 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.4.1-SNAPSHOT</version>
+    <version>6.5.1-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.test</artifactId>
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/Unaffected_PostImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/Unaffected_PostImage
new file mode 100644
index 0000000..6b664d9
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/Unaffected_PostImage
@@ -0,0 +1,2 @@
+This file
+should not be changed.
\ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/Unaffected_PreImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/Unaffected_PreImage
new file mode 100644
index 0000000..6b664d9
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/Unaffected_PreImage
@@ -0,0 +1,2 @@
+This file
+should not be changed.
\ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/XAndY.patch b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/XAndY.patch
new file mode 100644
index 0000000..35950f3
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/XAndY.patch
@@ -0,0 +1,32 @@
+diff --git a/X b/X
+index a3648a1..2d44096 100644
+--- a/X
++++ b/X
+@@ -2,2 +2,3 @@ a
+ b
++c
+ d
+@@ -16,4 +17,2 @@ p
+ q
+-r
+-s
+ t
+@@ -22,4 +21,8 @@ v
+ w
+-x
+-y
++0
++1
++2
++3
++4
++5
+ z
+diff --git a/Y b/Y
+index 2e65efe..7898192 100644
+--- a/Y
++++ b/Y
+@@ -1 +1 @@
+-a
+\ No newline at end of file
++a
\ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_add_nl.patch b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_add_nl.patch
new file mode 100644
index 0000000..444f7f7
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_add_nl.patch
@@ -0,0 +1,11 @@
+diff --git a/x_add_nl b/x_add_nl
+index 33a3e0e..71ac1b5 100644
+--- a/x_add_nl
++++ b/x_add_nl
+@@ -5,4 +5,4 @@ d
+ e
+ f
+ g
+-h
+\ No newline at end of file
++h
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_add_nl_PostImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_add_nl_PostImage
new file mode 100644
index 0000000..71ac1b5
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_add_nl_PostImage
@@ -0,0 +1,8 @@
+a
+b
+c
+d
+e
+f
+g
+h
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_add_nl_PreImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_add_nl_PreImage
new file mode 100644
index 0000000..33a3e0e
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_add_nl_PreImage
@@ -0,0 +1,8 @@
+a
+b
+c
+d
+e
+f
+g
+h
\ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_add_nl_crlf.patch b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_add_nl_crlf.patch
new file mode 100644
index 0000000..503d345
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_add_nl_crlf.patch
@@ -0,0 +1,11 @@
+diff --git a/x_add_nl_crlf b/x_add_nl_crlf
+index 33a3e0e..71ac1b5 100644
+--- a/x_add_nl_crlf
++++ b/x_add_nl_crlf
+@@ -5,4 +5,4 @@ d
+ e
+ f
+ g
+-h
+\ No newline at end of file
++h
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_add_nl_crlf_PostImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_add_nl_crlf_PostImage
new file mode 100644
index 0000000..95801b0
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_add_nl_crlf_PostImage
@@ -0,0 +1,8 @@
+a

+b

+c

+d

+e

+f

+g

+h

diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_add_nl_crlf_PreImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_add_nl_crlf_PreImage
new file mode 100644
index 0000000..a8a98da
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_add_nl_crlf_PreImage
@@ -0,0 +1,8 @@
+a

+b

+c

+d

+e

+f

+g

+h
\ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_d.patch b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_d.patch
new file mode 100644
index 0000000..cc90256
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_d.patch
@@ -0,0 +1,13 @@
+diff --git a/x_d b/x_d
+index 33a3e0e..9d2bc43 100644
+--- a/x_d
++++ b/x_d
+@@ -1,7 +1,7 @@
+ a
+ b
+ c
+-d
++D
+ e
+ f
+ g
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_d_PostImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_d_PostImage
new file mode 100644
index 0000000..9d2bc43
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_d_PostImage
@@ -0,0 +1,8 @@
+a
+b
+c
+D
+e
+f
+g
+h
\ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_d_PreImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_d_PreImage
new file mode 100644
index 0000000..33a3e0e
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_d_PreImage
@@ -0,0 +1,8 @@
+a
+b
+c
+d
+e
+f
+g
+h
\ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_d_crlf.patch b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_d_crlf.patch
new file mode 100644
index 0000000..8ec3f8b
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_d_crlf.patch
@@ -0,0 +1,13 @@
+diff --git a/x_d_crlf b/x_d_crlf
+index 33a3e0e..9d2bc43 100644
+--- a/x_d_crlf
++++ b/x_d_crlf
+@@ -1,7 +1,7 @@
+ a
+ b
+ c
+-d
++D
+ e
+ f
+ g
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_d_crlf_PostImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_d_crlf_PostImage
new file mode 100644
index 0000000..ecae1d6
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_d_crlf_PostImage
@@ -0,0 +1,8 @@
+a

+b

+c

+D

+e

+f

+g

+h
\ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_d_crlf_PreImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_d_crlf_PreImage
new file mode 100644
index 0000000..a8a98da
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_d_crlf_PreImage
@@ -0,0 +1,8 @@
+a

+b

+c

+d

+e

+f

+g

+h
\ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_e.patch b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_e.patch
new file mode 100644
index 0000000..413e4f8
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_e.patch
@@ -0,0 +1,14 @@
+diff --git a/x_e b/x_e
+index 33a3e0e..b3ab996 100644
+--- a/x_e
++++ b/x_e
+@@ -2,7 +2,7 @@ a
+ b
+ c
+ d
+-e
++E
+ f
+ g
+ h
+\ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_e_PostImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_e_PostImage
new file mode 100644
index 0000000..b3ab996
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_e_PostImage
@@ -0,0 +1,8 @@
+a
+b
+c
+d
+E
+f
+g
+h
\ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_e_PreImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_e_PreImage
new file mode 100644
index 0000000..33a3e0e
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_e_PreImage
@@ -0,0 +1,8 @@
+a
+b
+c
+d
+e
+f
+g
+h
\ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_e_crlf.patch b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_e_crlf.patch
new file mode 100644
index 0000000..e674454
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_e_crlf.patch
@@ -0,0 +1,14 @@
+diff --git a/x_e_crlf b/x_e_crlf
+index 33a3e0e..b3ab996 100644
+--- a/x_e_crlf
++++ b/x_e_crlf
+@@ -2,7 +2,7 @@ a
+ b
+ c
+ d
+-e
++E
+ f
+ g
+ h
+\ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_e_crlf_PostImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_e_crlf_PostImage
new file mode 100644
index 0000000..d327752
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_e_crlf_PostImage
@@ -0,0 +1,8 @@
+a

+b

+c

+d

+E

+f

+g

+h
\ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_e_crlf_PreImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_e_crlf_PreImage
new file mode 100644
index 0000000..a8a98da
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_e_crlf_PreImage
@@ -0,0 +1,8 @@
+a

+b

+c

+d

+e

+f

+g

+h
\ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_last_rm_nl.patch b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_last_rm_nl.patch
new file mode 100644
index 0000000..34ed246
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_last_rm_nl.patch
@@ -0,0 +1,15 @@
+diff --git a/x_last_rm_nl b/x_last_rm_nl
+index 71ac1b5..b3ab996 100644
+--- a/x_last_rm_nl
++++ b/x_last_rm_nl
+@@ -2,7 +2,7 @@ a
+ b
+ c
+ d
+-e
++E
+ f
+ g
+-h
++h
+\ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_last_rm_nl_PostImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_last_rm_nl_PostImage
new file mode 100644
index 0000000..b3ab996
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_last_rm_nl_PostImage
@@ -0,0 +1,8 @@
+a
+b
+c
+d
+E
+f
+g
+h
\ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_last_rm_nl_PreImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_last_rm_nl_PreImage
new file mode 100644
index 0000000..71ac1b5
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_last_rm_nl_PreImage
@@ -0,0 +1,8 @@
+a
+b
+c
+d
+e
+f
+g
+h
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_last_rm_nl_crlf.patch b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_last_rm_nl_crlf.patch
new file mode 100644
index 0000000..3f74024
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_last_rm_nl_crlf.patch
@@ -0,0 +1,15 @@
+diff --git a/x_last_rm_nl_crlf b/x_last_rm_nl_crlf
+index 71ac1b5..b3ab996 100644
+--- a/x_last_rm_nl_crlf
++++ b/x_last_rm_nl_crlf
+@@ -2,7 +2,7 @@ a
+ b
+ c
+ d
+-e
++E
+ f
+ g
+-h
++h
+\ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_last_rm_nl_crlf_PostImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_last_rm_nl_crlf_PostImage
new file mode 100644
index 0000000..d327752
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_last_rm_nl_crlf_PostImage
@@ -0,0 +1,8 @@
+a

+b

+c

+d

+E

+f

+g

+h
\ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_last_rm_nl_crlf_PreImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_last_rm_nl_crlf_PreImage
new file mode 100644
index 0000000..95801b0
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/x_last_rm_nl_crlf_PreImage
@@ -0,0 +1,8 @@
+a

+b

+c

+d

+e

+f

+g

+h

diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/z_e.patch b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/z_e.patch
new file mode 100644
index 0000000..f531033
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/z_e.patch
@@ -0,0 +1,297 @@
+diff --git a/z_e b/z_e
+index 8d8786f..7888356 100644
+--- a/z_e
++++ b/z_e
+@@ -20,6 +20,7 @@
+ package org.jsonschema2pojo.util;
+ 
+ import java.util.ArrayList;
++import java.util.Collections;
+ import java.util.List;
+ import java.util.regex.Matcher;
+ import java.util.regex.Pattern;
+@@ -36,76 +37,81 @@
+     private static final Pattern UNDERSCORE_PATTERN_1 = Pattern.compile("([A-Z]+)([A-Z][a-z])");
+     private static final Pattern UNDERSCORE_PATTERN_2 = Pattern.compile("([a-z\\d])([A-Z])");
+ 
+-    private List<RuleAndReplacement> plurals = new ArrayList<RuleAndReplacement>();
+-    private List<RuleAndReplacement> singulars = new ArrayList<RuleAndReplacement>();
+-    private List<String> uncountables = new ArrayList<String>();
++    private final List<RuleAndReplacement> plurals;
++    private final List<RuleAndReplacement> singulars;
++    private final List<String> uncountables;
+ 
+-    private static Inflector instance  = new Inflector();
++    private static Inflector instance  = createDefaultBuilder().build();
+ 
+-    private Inflector() {
+-        // Woo, you can't touch me.
+-
+-        initialize();
++    private Inflector(Builder builder) {
++        plurals = Collections.unmodifiableList(builder.plurals);
++        singulars = Collections.unmodifiableList(builder.singulars);
++        uncountables = Collections.unmodifiableList(builder.uncountables);
+     }
+ 
+-    private void initialize() {
+-        plural("$", "s");
+-        plural("s$", "s");
+-        plural("(ax|test)is$", "$1es");
+-        plural("(octop|vir)us$", "$1i");
+-        plural("(alias|status)$", "$1es");
+-        plural("(bu)s$", "$1es");
+-        plural("(buffal|tomat)o$", "$1oes");
+-        plural("([ti])um$", "$1a");
+-        plural("sis$", "ses");
+-        plural("(?:([^f])fe|([lr])f)$", "$1$2ves");
+-        plural("(hive)$", "$1s");
+-        plural("([^aeiouy]|qu)y$", "$1ies");
+-        plural("([^aeiouy]|qu)ies$", "$1y");
+-        plural("(x|ch|ss|sh)$", "$1es");
+-        plural("(matr|vert|ind)ix|ex$", "$1ices");
+-        plural("([m|l])ouse$", "$1ice");
+-        plural("(ox)$", "$1en");
+-        plural("(quiz)$", "$1zes");
++    public static Inflector.Builder createDefaultBuilder()
++    {
++        Builder builder = builder();
+ 
+-        singular("s$", "");
+-        singular("(n)ews$", "$1ews");
+-        singular("([ti])a$", "$1um");
+-        singular("((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$", "$1$2sis");
+-        singular("(^analy)ses$", "$1sis");
+-        singular("([^f])ves$", "$1fe");
+-        singular("(hive)s$", "$1");
+-        singular("(tive)s$", "$1");
+-        singular("([lr])ves$", "$1f");
+-        singular("([^aeiouy]|qu)ies$", "$1y");
+-        singular("(s)eries$", "$1eries");
+-        singular("(m)ovies$", "$1ovie");
+-        singular("(x|ch|ss|sh)es$", "$1");
+-        singular("([m|l])ice$", "$1ouse");
+-        singular("(bus)es$", "$1");
+-        singular("(o)es$", "$1");
+-        singular("(shoe)s$", "$1");
+-        singular("(cris|ax|test)es$", "$1is");
+-        singular("([octop|vir])i$", "$1us");
+-        singular("(alias|status)es$", "$1");
+-        singular("^(ox)en", "$1");
+-        singular("(vert|ind)ices$", "$1ex");
+-        singular("(matr)ices$", "$1ix");
+-        singular("(quiz)zes$", "$1");
+-        singular("(ess)$", "$1");
++        builder.plural("$", "s")
++            .plural("s$", "s")
++            .plural("(ax|test)is$", "$1es")
++            .plural("(octop|vir)us$", "$1i")
++            .plural("(alias|status)$", "$1es")
++            .plural("(bu)s$", "$1es")
++            .plural("(buffal|tomat)o$", "$1oes")
++            .plural("([ti])um$", "$1a")
++            .plural("sis$", "ses")
++            .plural("(?:([^f])fe|([lr])f)$", "$1$2ves")
++            .plural("(hive)$", "$1s")
++            .plural("([^aeiouy]|qu)y$", "$1ies")
++            .plural("([^aeiouy]|qu)ies$", "$1y")
++            .plural("(x|ch|ss|sh)$", "$1es")
++            .plural("(matr|vert|ind)ix|ex$", "$1ices")
++            .plural("([m|l])ouse$", "$1ice")
++            .plural("(ox)$", "$1en")
++            .plural("(quiz)$", "$1zes");
+ 
+-        singular("men$", "man");
+-        plural("man$", "men");
++        builder.singular("s$", "")
++            .singular("(n)ews$", "$1ews")
++            .singular("([ti])a$", "$1um")
++            .singular("((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$", "$1$2sis")
++            .singular("(^analy)ses$", "$1sis")
++            .singular("([^f])ves$", "$1fe")
++            .singular("(hive)s$", "$1")
++            .singular("(tive)s$", "$1")
++            .singular("([lr])ves$", "$1f")
++            .singular("([^aeiouy]|qu)ies$", "$1y")
++            .singular("(s)eries$", "$1eries")
++            .singular("(m)ovies$", "$1ovie")
++            .singular("(x|ch|ss|sh)es$", "$1")
++            .singular("([m|l])ice$", "$1ouse")
++            .singular("(bus)es$", "$1")
++            .singular("(o)es$", "$1")
++            .singular("(shoe)s$", "$1")
++            .singular("(cris|ax|test)es$", "$1is")
++            .singular("([octop|vir])i$", "$1us")
++            .singular("(alias|status)es$", "$1")
++            .singular("^(ox)en", "$1")
++            .singular("(vert|ind)ices$", "$1ex")
++            .singular("(matr)ices$", "$1ix")
++            .singular("(quiz)zes$", "$1")
++            .singular("(ess)$", "$1");
+ 
+-        irregular("curve", "curves");
+-        irregular("leaf", "leaves");
+-        irregular("roof", "rooves");
+-        irregular("person", "people");
+-        irregular("child", "children");
+-        irregular("sex", "sexes");
+-        irregular("move", "moves");
++        builder.singular("men$", "man")
++            .plural("man$", "men");
+ 
+-        uncountable(new String[] { "equipment", "information", "rice", "money", "species", "series", "fish", "sheep", "s" });
++        builder.irregular("curve", "curves")
++            .irregular("leaf", "leaves")
++            .irregular("roof", "rooves")
++            .irregular("person", "people")
++            .irregular("child", "children")
++            .irregular("sex", "sexes")
++            .irregular("move", "moves");
++
++        builder.uncountable(new String[] { "equipment", "information", "rice", "money", "species", "series", "fish", "sheep", "s" });
++
++        return builder;
+     }
+ 
+     public static Inflector getInstance() {
+@@ -122,28 +128,27 @@
+         return underscoredWord;
+     }
+ 
+-    public synchronized String pluralize(String word) {
++    public String pluralize(String word) {
+         if (uncountables.contains(word.toLowerCase())) {
+             return word;
+         }
+         return replaceWithFirstRule(word, plurals);
+     }
+ 
+-    public synchronized String singularize(String word) {
++    public String singularize(String word) {
+         if (uncountables.contains(word.toLowerCase())) {
+             return word;
+         }
+         return replaceWithFirstRule(word, singulars);
+     }
+ 
+-    private String replaceWithFirstRule(String word, List<RuleAndReplacement> ruleAndReplacements) {
++    private static String replaceWithFirstRule(String word, List<RuleAndReplacement> ruleAndReplacements) {
+ 
+         for (RuleAndReplacement rar : ruleAndReplacements) {
+-            String rule = rar.getRule();
+             String replacement = rar.getReplacement();
+ 
+             // Return if we find a match.
+-            Matcher matcher = Pattern.compile(rule, Pattern.CASE_INSENSITIVE).matcher(word);
++            Matcher matcher = rar.getPattern().matcher(word);
+             if (matcher.find()) {
+                 return matcher.replaceAll(replacement);
+             }
+@@ -161,49 +166,68 @@
+         return tableize(className);
+     }
+ 
+-    private void plural(String rule, String replacement) {
+-        plurals.add(0, new RuleAndReplacement(rule, replacement));
++    public static Builder builder()
++    {
++        return new Builder();
+     }
+ 
+-    private void singular(String rule, String replacement) {
+-        singulars.add(0, new RuleAndReplacement(rule, replacement));
++    // Ugh, no open structs in Java (not-natively at least).
++    private static class RuleAndReplacement {
++        private final String rule;
++        private final String replacement;
++        private final Pattern pattern;
++
++        public RuleAndReplacement(String rule, String replacement) {
++            this.rule = rule;
++            this.replacement = replacement;
++            this.pattern = Pattern.compile(rule, Pattern.CASE_INSENSITIVE);
++        }
++
++        public String getReplacement() {
++            return replacement;
++        }
++
++        public String getRule() {
++            return rule;
++        }
++
++        public Pattern getPattern() {
++            return pattern;
++        }
+     }
+ 
+-    private void irregular(String singular, String plural) {
+-        plural(singular, plural);
+-        singular(plural, singular);
+-    }
++    public static class Builder
++    {
++        private List<RuleAndReplacement> plurals = new ArrayList<RuleAndReplacement>();
++        private List<RuleAndReplacement> singulars = new ArrayList<RuleAndReplacement>();
++        private List<String> uncountables = new ArrayList<String>();
+ 
+-    private void uncountable(String... words) {
+-        for (String word : words) {
+-            uncountables.add(word);
++        public Builder plural(String rule, String replacement) {
++            plurals.add(0, new RuleAndReplacement(rule, replacement));
++            return this;
++        }
++
++        public Builder singular(String rule, String replacement) {
++            singulars.add(0, new RuleAndReplacement(rule, replacement));
++            return this;
++        }
++
++        public Builder irregular(String singular, String plural) {
++            plural(singular, plural);
++            singular(plural, singular);
++            return this;
++        }
++
++        public Builder uncountable(String... words) {
++            for (String word : words) {
++                uncountables.add(word);
++            }
++            return this;
++        }
++
++        public Inflector build()
++        {
++            return new Inflector(this);
+         }
+     }
+ }
+-
+-// Ugh, no open structs in Java (not-natively at least).
+-class RuleAndReplacement {
+-    private String rule;
+-    private String replacement;
+-
+-    public RuleAndReplacement(String rule, String replacement) {
+-        this.rule = rule;
+-        this.replacement = replacement;
+-    }
+-
+-    public String getReplacement() {
+-        return replacement;
+-    }
+-
+-    public void setReplacement(String replacement) {
+-        this.replacement = replacement;
+-    }
+-
+-    public String getRule() {
+-        return rule;
+-    }
+-
+-    public void setRule(String rule) {
+-        this.rule = rule;
+-    }
+-}
+\ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/z_e_PostImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/z_e_PostImage
new file mode 100644
index 0000000..7888356
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/z_e_PostImage
@@ -0,0 +1,233 @@
+/**
+ * Copyright © 2007 Chu Yeow Cheah
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ * Copied verbatim from http://dzone.com/snippets/java-inflections, used 
+ * and licensed with express permission from the author Chu Yeow Cheah.
+ */
+
+package org.jsonschema2pojo.util;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Transforms words (from singular to plural, from camelCase to under_score,
+ * etc.). I got bored of doing Real Work...
+ * 
+ * @author chuyeow
+ */
+public class Inflector {
+
+    // Pfft, can't think of a better name, but this is needed to avoid the price of initializing the pattern on each call.
+    private static final Pattern UNDERSCORE_PATTERN_1 = Pattern.compile("([A-Z]+)([A-Z][a-z])");
+    private static final Pattern UNDERSCORE_PATTERN_2 = Pattern.compile("([a-z\\d])([A-Z])");
+
+    private final List<RuleAndReplacement> plurals;
+    private final List<RuleAndReplacement> singulars;
+    private final List<String> uncountables;
+
+    private static Inflector instance  = createDefaultBuilder().build();
+
+    private Inflector(Builder builder) {
+        plurals = Collections.unmodifiableList(builder.plurals);
+        singulars = Collections.unmodifiableList(builder.singulars);
+        uncountables = Collections.unmodifiableList(builder.uncountables);
+    }
+
+    public static Inflector.Builder createDefaultBuilder()
+    {
+        Builder builder = builder();
+
+        builder.plural("$", "s")
+            .plural("s$", "s")
+            .plural("(ax|test)is$", "$1es")
+            .plural("(octop|vir)us$", "$1i")
+            .plural("(alias|status)$", "$1es")
+            .plural("(bu)s$", "$1es")
+            .plural("(buffal|tomat)o$", "$1oes")
+            .plural("([ti])um$", "$1a")
+            .plural("sis$", "ses")
+            .plural("(?:([^f])fe|([lr])f)$", "$1$2ves")
+            .plural("(hive)$", "$1s")
+            .plural("([^aeiouy]|qu)y$", "$1ies")
+            .plural("([^aeiouy]|qu)ies$", "$1y")
+            .plural("(x|ch|ss|sh)$", "$1es")
+            .plural("(matr|vert|ind)ix|ex$", "$1ices")
+            .plural("([m|l])ouse$", "$1ice")
+            .plural("(ox)$", "$1en")
+            .plural("(quiz)$", "$1zes");
+
+        builder.singular("s$", "")
+            .singular("(n)ews$", "$1ews")
+            .singular("([ti])a$", "$1um")
+            .singular("((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$", "$1$2sis")
+            .singular("(^analy)ses$", "$1sis")
+            .singular("([^f])ves$", "$1fe")
+            .singular("(hive)s$", "$1")
+            .singular("(tive)s$", "$1")
+            .singular("([lr])ves$", "$1f")
+            .singular("([^aeiouy]|qu)ies$", "$1y")
+            .singular("(s)eries$", "$1eries")
+            .singular("(m)ovies$", "$1ovie")
+            .singular("(x|ch|ss|sh)es$", "$1")
+            .singular("([m|l])ice$", "$1ouse")
+            .singular("(bus)es$", "$1")
+            .singular("(o)es$", "$1")
+            .singular("(shoe)s$", "$1")
+            .singular("(cris|ax|test)es$", "$1is")
+            .singular("([octop|vir])i$", "$1us")
+            .singular("(alias|status)es$", "$1")
+            .singular("^(ox)en", "$1")
+            .singular("(vert|ind)ices$", "$1ex")
+            .singular("(matr)ices$", "$1ix")
+            .singular("(quiz)zes$", "$1")
+            .singular("(ess)$", "$1");
+
+        builder.singular("men$", "man")
+            .plural("man$", "men");
+
+        builder.irregular("curve", "curves")
+            .irregular("leaf", "leaves")
+            .irregular("roof", "rooves")
+            .irregular("person", "people")
+            .irregular("child", "children")
+            .irregular("sex", "sexes")
+            .irregular("move", "moves");
+
+        builder.uncountable(new String[] { "equipment", "information", "rice", "money", "species", "series", "fish", "sheep", "s" });
+
+        return builder;
+    }
+
+    public static Inflector getInstance() {
+        return instance;
+    }
+
+    private String underscore(String camelCasedWord) {
+
+        // Regexes in Java are fucking stupid...
+        String underscoredWord = UNDERSCORE_PATTERN_1.matcher(camelCasedWord).replaceAll("$1_$2");
+        underscoredWord = UNDERSCORE_PATTERN_2.matcher(underscoredWord).replaceAll("$1_$2");
+        underscoredWord = underscoredWord.replace('-', '_').toLowerCase();
+
+        return underscoredWord;
+    }
+
+    public String pluralize(String word) {
+        if (uncountables.contains(word.toLowerCase())) {
+            return word;
+        }
+        return replaceWithFirstRule(word, plurals);
+    }
+
+    public String singularize(String word) {
+        if (uncountables.contains(word.toLowerCase())) {
+            return word;
+        }
+        return replaceWithFirstRule(word, singulars);
+    }
+
+    private static String replaceWithFirstRule(String word, List<RuleAndReplacement> ruleAndReplacements) {
+
+        for (RuleAndReplacement rar : ruleAndReplacements) {
+            String replacement = rar.getReplacement();
+
+            // Return if we find a match.
+            Matcher matcher = rar.getPattern().matcher(word);
+            if (matcher.find()) {
+                return matcher.replaceAll(replacement);
+            }
+        }
+        return word;
+    }
+
+    private String tableize(String className) {
+        return pluralize(underscore(className));
+    }
+
+    private String tableize(Class<?> klass) {
+        // Strip away package name - we only want the 'base' class name.
+        String className = klass.getName().replace(klass.getPackage().getName() + ".", "");
+        return tableize(className);
+    }
+
+    public static Builder builder()
+    {
+        return new Builder();
+    }
+
+    // Ugh, no open structs in Java (not-natively at least).
+    private static class RuleAndReplacement {
+        private final String rule;
+        private final String replacement;
+        private final Pattern pattern;
+
+        public RuleAndReplacement(String rule, String replacement) {
+            this.rule = rule;
+            this.replacement = replacement;
+            this.pattern = Pattern.compile(rule, Pattern.CASE_INSENSITIVE);
+        }
+
+        public String getReplacement() {
+            return replacement;
+        }
+
+        public String getRule() {
+            return rule;
+        }
+
+        public Pattern getPattern() {
+            return pattern;
+        }
+    }
+
+    public static class Builder
+    {
+        private List<RuleAndReplacement> plurals = new ArrayList<RuleAndReplacement>();
+        private List<RuleAndReplacement> singulars = new ArrayList<RuleAndReplacement>();
+        private List<String> uncountables = new ArrayList<String>();
+
+        public Builder plural(String rule, String replacement) {
+            plurals.add(0, new RuleAndReplacement(rule, replacement));
+            return this;
+        }
+
+        public Builder singular(String rule, String replacement) {
+            singulars.add(0, new RuleAndReplacement(rule, replacement));
+            return this;
+        }
+
+        public Builder irregular(String singular, String plural) {
+            plural(singular, plural);
+            singular(plural, singular);
+            return this;
+        }
+
+        public Builder uncountable(String... words) {
+            for (String word : words) {
+                uncountables.add(word);
+            }
+            return this;
+        }
+
+        public Inflector build()
+        {
+            return new Inflector(this);
+        }
+    }
+}
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/z_e_PreImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/z_e_PreImage
new file mode 100644
index 0000000..8d8786f
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/z_e_PreImage
@@ -0,0 +1,209 @@
+/**
+ * Copyright © 2007 Chu Yeow Cheah
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ * Copied verbatim from http://dzone.com/snippets/java-inflections, used 
+ * and licensed with express permission from the author Chu Yeow Cheah.
+ */
+
+package org.jsonschema2pojo.util;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Transforms words (from singular to plural, from camelCase to under_score,
+ * etc.). I got bored of doing Real Work...
+ * 
+ * @author chuyeow
+ */
+public class Inflector {
+
+    // Pfft, can't think of a better name, but this is needed to avoid the price of initializing the pattern on each call.
+    private static final Pattern UNDERSCORE_PATTERN_1 = Pattern.compile("([A-Z]+)([A-Z][a-z])");
+    private static final Pattern UNDERSCORE_PATTERN_2 = Pattern.compile("([a-z\\d])([A-Z])");
+
+    private List<RuleAndReplacement> plurals = new ArrayList<RuleAndReplacement>();
+    private List<RuleAndReplacement> singulars = new ArrayList<RuleAndReplacement>();
+    private List<String> uncountables = new ArrayList<String>();
+
+    private static Inflector instance  = new Inflector();
+
+    private Inflector() {
+        // Woo, you can't touch me.
+
+        initialize();
+    }
+
+    private void initialize() {
+        plural("$", "s");
+        plural("s$", "s");
+        plural("(ax|test)is$", "$1es");
+        plural("(octop|vir)us$", "$1i");
+        plural("(alias|status)$", "$1es");
+        plural("(bu)s$", "$1es");
+        plural("(buffal|tomat)o$", "$1oes");
+        plural("([ti])um$", "$1a");
+        plural("sis$", "ses");
+        plural("(?:([^f])fe|([lr])f)$", "$1$2ves");
+        plural("(hive)$", "$1s");
+        plural("([^aeiouy]|qu)y$", "$1ies");
+        plural("([^aeiouy]|qu)ies$", "$1y");
+        plural("(x|ch|ss|sh)$", "$1es");
+        plural("(matr|vert|ind)ix|ex$", "$1ices");
+        plural("([m|l])ouse$", "$1ice");
+        plural("(ox)$", "$1en");
+        plural("(quiz)$", "$1zes");
+
+        singular("s$", "");
+        singular("(n)ews$", "$1ews");
+        singular("([ti])a$", "$1um");
+        singular("((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$", "$1$2sis");
+        singular("(^analy)ses$", "$1sis");
+        singular("([^f])ves$", "$1fe");
+        singular("(hive)s$", "$1");
+        singular("(tive)s$", "$1");
+        singular("([lr])ves$", "$1f");
+        singular("([^aeiouy]|qu)ies$", "$1y");
+        singular("(s)eries$", "$1eries");
+        singular("(m)ovies$", "$1ovie");
+        singular("(x|ch|ss|sh)es$", "$1");
+        singular("([m|l])ice$", "$1ouse");
+        singular("(bus)es$", "$1");
+        singular("(o)es$", "$1");
+        singular("(shoe)s$", "$1");
+        singular("(cris|ax|test)es$", "$1is");
+        singular("([octop|vir])i$", "$1us");
+        singular("(alias|status)es$", "$1");
+        singular("^(ox)en", "$1");
+        singular("(vert|ind)ices$", "$1ex");
+        singular("(matr)ices$", "$1ix");
+        singular("(quiz)zes$", "$1");
+        singular("(ess)$", "$1");
+
+        singular("men$", "man");
+        plural("man$", "men");
+
+        irregular("curve", "curves");
+        irregular("leaf", "leaves");
+        irregular("roof", "rooves");
+        irregular("person", "people");
+        irregular("child", "children");
+        irregular("sex", "sexes");
+        irregular("move", "moves");
+
+        uncountable(new String[] { "equipment", "information", "rice", "money", "species", "series", "fish", "sheep", "s" });
+    }
+
+    public static Inflector getInstance() {
+        return instance;
+    }
+
+    private String underscore(String camelCasedWord) {
+
+        // Regexes in Java are fucking stupid...
+        String underscoredWord = UNDERSCORE_PATTERN_1.matcher(camelCasedWord).replaceAll("$1_$2");
+        underscoredWord = UNDERSCORE_PATTERN_2.matcher(underscoredWord).replaceAll("$1_$2");
+        underscoredWord = underscoredWord.replace('-', '_').toLowerCase();
+
+        return underscoredWord;
+    }
+
+    public synchronized String pluralize(String word) {
+        if (uncountables.contains(word.toLowerCase())) {
+            return word;
+        }
+        return replaceWithFirstRule(word, plurals);
+    }
+
+    public synchronized String singularize(String word) {
+        if (uncountables.contains(word.toLowerCase())) {
+            return word;
+        }
+        return replaceWithFirstRule(word, singulars);
+    }
+
+    private String replaceWithFirstRule(String word, List<RuleAndReplacement> ruleAndReplacements) {
+
+        for (RuleAndReplacement rar : ruleAndReplacements) {
+            String rule = rar.getRule();
+            String replacement = rar.getReplacement();
+
+            // Return if we find a match.
+            Matcher matcher = Pattern.compile(rule, Pattern.CASE_INSENSITIVE).matcher(word);
+            if (matcher.find()) {
+                return matcher.replaceAll(replacement);
+            }
+        }
+        return word;
+    }
+
+    private String tableize(String className) {
+        return pluralize(underscore(className));
+    }
+
+    private String tableize(Class<?> klass) {
+        // Strip away package name - we only want the 'base' class name.
+        String className = klass.getName().replace(klass.getPackage().getName() + ".", "");
+        return tableize(className);
+    }
+
+    private void plural(String rule, String replacement) {
+        plurals.add(0, new RuleAndReplacement(rule, replacement));
+    }
+
+    private void singular(String rule, String replacement) {
+        singulars.add(0, new RuleAndReplacement(rule, replacement));
+    }
+
+    private void irregular(String singular, String plural) {
+        plural(singular, plural);
+        singular(plural, singular);
+    }
+
+    private void uncountable(String... words) {
+        for (String word : words) {
+            uncountables.add(word);
+        }
+    }
+}
+
+// Ugh, no open structs in Java (not-natively at least).
+class RuleAndReplacement {
+    private String rule;
+    private String replacement;
+
+    public RuleAndReplacement(String rule, String replacement) {
+        this.rule = rule;
+        this.replacement = replacement;
+    }
+
+    public String getReplacement() {
+        return replacement;
+    }
+
+    public void setReplacement(String replacement) {
+        this.replacement = replacement;
+    }
+
+    public String getRule() {
+        return rule;
+    }
+
+    public void setRule(String rule) {
+        this.rule = rule;
+    }
+}
\ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/z_e_add_nl.patch b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/z_e_add_nl.patch
new file mode 100644
index 0000000..0fe4b45
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/z_e_add_nl.patch
@@ -0,0 +1,17 @@
+diff --git a/z_e_add_nl b/z_e_add_nl
+index 33a3e0e..274cb0e 100644
+--- a/z_e_add_nl
++++ b/z_e_add_nl
+@@ -2,7 +2,7 @@ a
+ b
+ c
+ d
+-e
+-f
+-g
+-h
+\ No newline at end of file
++E
++F
++G
++H
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/z_e_add_nl_PostImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/z_e_add_nl_PostImage
new file mode 100644
index 0000000..274cb0e
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/z_e_add_nl_PostImage
@@ -0,0 +1,8 @@
+a
+b
+c
+d
+E
+F
+G
+H
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/z_e_add_nl_PreImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/z_e_add_nl_PreImage
new file mode 100644
index 0000000..33a3e0e
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/z_e_add_nl_PreImage
@@ -0,0 +1,8 @@
+a
+b
+c
+d
+e
+f
+g
+h
\ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/z_e_no_nl.patch b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/z_e_no_nl.patch
new file mode 100644
index 0000000..bce13e4
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/z_e_no_nl.patch
@@ -0,0 +1,18 @@
+diff --git a/z_e_no_nl b/z_e_no_nl
+index 33a3e0e..234fedc 100644
+--- a/z_e_no_nl
++++ b/z_e_no_nl
+@@ -2,7 +2,7 @@ a
+ b
+ c
+ d
+-e
+-f
+-g
+-h
+\ No newline at end of file
++E
++F
++G
++H
+\ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/z_e_no_nl_PostImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/z_e_no_nl_PostImage
new file mode 100644
index 0000000..234fedc
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/z_e_no_nl_PostImage
@@ -0,0 +1,8 @@
+a
+b
+c
+d
+E
+F
+G
+H
\ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/z_e_no_nl_PreImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/z_e_no_nl_PreImage
new file mode 100644
index 0000000..33a3e0e
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/z_e_no_nl_PreImage
@@ -0,0 +1,8 @@
+a
+b
+c
+d
+e
+f
+g
+h
\ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/z_e_rm_nl.patch b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/z_e_rm_nl.patch
new file mode 100644
index 0000000..d669706
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/z_e_rm_nl.patch
@@ -0,0 +1,17 @@
+diff --git a/z_e_rm_nl b/z_e_rm_nl
+index 71ac1b5..234fedc 100644
+--- a/z_e_rm_nl
++++ b/z_e_rm_nl
+@@ -2,7 +2,7 @@ a
+ b
+ c
+ d
+-e
+-f
+-g
+-h
++E
++F
++G
++H
+\ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/z_e_rm_nl_PostImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/z_e_rm_nl_PostImage
new file mode 100644
index 0000000..234fedc
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/z_e_rm_nl_PostImage
@@ -0,0 +1,8 @@
+a
+b
+c
+d
+E
+F
+G
+H
\ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/z_e_rm_nl_PreImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/z_e_rm_nl_PreImage
new file mode 100644
index 0000000..71ac1b5
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/z_e_rm_nl_PreImage
@@ -0,0 +1,8 @@
+a
+b
+c
+d
+e
+f
+g
+h
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/commit-graph.v1 b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/commit-graph.v1
new file mode 100644
index 0000000..941c0a7
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/commit-graph.v1
Binary files differ
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java
index 6a84f0a..12300b3 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java
@@ -378,44 +378,59 @@
 	}
 
 	private enum TestPullMode {
-		MERGE, REBASE, REBASE_PREASERVE
+		MERGE, REBASE, REBASE_MERGES
 	}
 
 	@Test
 	/** global rebase config should be respected */
-	public void testPullWithRebasePreserve1Config() throws Exception {
+	public void testPullWithRebaseMerges1Config() throws Exception {
+		Callable<PullResult> setup = () -> {
+			StoredConfig config = dbTarget.getConfig();
+			config.setString("pull", null, "rebase", "merges");
+			config.save();
+			return target.pull().call();
+		};
+		doTestPullWithRebase(setup, TestPullMode.REBASE_MERGES);
+	}
+
+	@Test
+	/**
+	 * global rebase config using old "preserve" value which was renamed to
+	 * "merges" should be respected to ensure backwards compatibility
+	 */
+	public void testPullWithRebaseMerges1ConfigAlias() throws Exception {
 		Callable<PullResult> setup = () -> {
 			StoredConfig config = dbTarget.getConfig();
 			config.setString("pull", null, "rebase", "preserve");
 			config.save();
 			return target.pull().call();
 		};
-		doTestPullWithRebase(setup, TestPullMode.REBASE_PREASERVE);
+		doTestPullWithRebase(setup, TestPullMode.REBASE_MERGES);
 	}
 
 	@Test
 	/** the branch-local config should win over the global config */
-	public void testPullWithRebasePreserveConfig2() throws Exception {
+	public void testPullWithRebaseMergesConfig2() throws Exception {
 		Callable<PullResult> setup = () -> {
 			StoredConfig config = dbTarget.getConfig();
 			config.setString("pull", null, "rebase", "false");
-			config.setString("branch", "master", "rebase", "preserve");
+			config.setString("branch", "master", "rebase", "merges");
 			config.save();
 			return target.pull().call();
 		};
-		doTestPullWithRebase(setup, TestPullMode.REBASE_PREASERVE);
+		doTestPullWithRebase(setup, TestPullMode.REBASE_MERGES);
 	}
 
 	@Test
 	/** the branch-local config should be respected */
-	public void testPullWithRebasePreserveConfig3() throws Exception {
+	public void testPullWithRebaseMergesConfig3() throws Exception {
 		Callable<PullResult> setup = () -> {
 			StoredConfig config = dbTarget.getConfig();
-			config.setString("branch", "master", "rebase", "preserve");
+			config.setString("branch", "master", "rebase", "merges");
 			config.save();
 			return target.pull().call();
 		};
-		doTestPullWithRebase(setup, TestPullMode.REBASE_PREASERVE);
+		doTestPullWithRebase(setup, TestPullMode.REBASE_MERGES);
 	}
 
 	@Test
@@ -435,7 +450,7 @@
 	public void testPullWithRebaseConfig2() throws Exception {
 		Callable<PullResult> setup = () -> {
 			StoredConfig config = dbTarget.getConfig();
-			config.setString("pull", null, "rebase", "preserve");
+			config.setString("pull", null, "rebase", "merges");
 			config.setString("branch", "master", "rebase", "true");
 			config.save();
 			return target.pull().call();
@@ -543,7 +558,7 @@
 				assertEquals(sourceCommit, next.getParent(1));
 				// since both parents are known do no further checks here
 			} else {
-				if (expectedPullMode == TestPullMode.REBASE_PREASERVE) {
+				if (expectedPullMode == TestPullMode.REBASE_MERGES) {
 					next = rw.next();
 					assertEquals(2, next.getParentCount());
 				}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphBuilderTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphBuilderTest.java
new file mode 100644
index 0000000..8ecf5df
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphBuilderTest.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2022, Tencent.
+ *
+ * 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.commitgraph;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+
+import org.junit.Test;
+
+public class CommitGraphBuilderTest {
+
+	@Test
+	public void testRepeatedChunk() throws Exception {
+		byte[] buffer = new byte[2048];
+
+		CommitGraphBuilder builder1 = CommitGraphBuilder.builder();
+		builder1.addOidFanout(buffer);
+		Exception e1 = assertThrows(CommitGraphFormatException.class, () -> {
+			builder1.addOidFanout(buffer);
+		});
+		assertEquals("commit-graph chunk id 0x4f494446 appears multiple times",
+				e1.getMessage());
+
+		CommitGraphBuilder builder2 = CommitGraphBuilder.builder();
+		builder2.addOidLookUp(buffer);
+		Exception e2 = assertThrows(CommitGraphFormatException.class, () -> {
+			builder2.addOidLookUp(buffer);
+		});
+		assertEquals("commit-graph chunk id 0x4f49444c appears multiple times",
+				e2.getMessage());
+
+		CommitGraphBuilder builder3 = CommitGraphBuilder.builder();
+		builder3.addCommitData(buffer);
+		Exception e3 = assertThrows(CommitGraphFormatException.class, () -> {
+			builder3.addCommitData(buffer);
+		});
+		assertEquals("commit-graph chunk id 0x43444154 appears multiple times",
+				e3.getMessage());
+
+		CommitGraphBuilder builder4 = CommitGraphBuilder.builder();
+		builder4.addExtraList(buffer);
+		Exception e4 = assertThrows(CommitGraphFormatException.class, () -> {
+			builder4.addExtraList(buffer);
+		});
+		assertEquals("commit-graph chunk id 0x45444745 appears multiple times",
+				e4.getMessage());
+	}
+
+	@Test
+	public void testNeededChunk() {
+		byte[] buffer = new byte[2048];
+
+		Exception e1 = assertThrows(CommitGraphFormatException.class, () -> {
+			CommitGraphBuilder.builder().addOidLookUp(buffer)
+					.addCommitData(buffer).build();
+		});
+		assertEquals("commit-graph 0x4f494446 chunk has not been loaded",
+				e1.getMessage());
+
+		Exception e2 = assertThrows(CommitGraphFormatException.class, () -> {
+			CommitGraphBuilder.builder().addOidFanout(buffer)
+					.addCommitData(buffer).build();
+		});
+		assertEquals("commit-graph 0x4f49444c chunk has not been loaded",
+				e2.getMessage());
+
+		Exception e3 = assertThrows(CommitGraphFormatException.class, () -> {
+			CommitGraphBuilder.builder().addOidFanout(buffer)
+					.addOidLookUp(buffer).build();
+		});
+		assertEquals("commit-graph 0x43444154 chunk has not been loaded",
+				e3.getMessage());
+	}
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphLoaderTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphLoaderTest.java
new file mode 100644
index 0000000..fd427a1
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphLoaderTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2022, Tencent.
+ *
+ * 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.commitgraph;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import org.eclipse.jgit.internal.storage.commitgraph.CommitGraph.CommitData;
+import org.eclipse.jgit.junit.JGitTestUtil;
+import org.eclipse.jgit.lib.ObjectId;
+import org.junit.Test;
+
+/**
+ * Test CommitGraphLoader by reading the commit-graph file generated by Cgit.
+ */
+public class CommitGraphLoaderTest {
+
+	private CommitGraph commitGraph;
+
+	@Test
+	public void readCommitGraphV1() throws Exception {
+		commitGraph = CommitGraphLoader
+				.open(JGitTestUtil.getTestResourceFile("commit-graph.v1"));
+		assertNotNull(commitGraph);
+		assertEquals(10, commitGraph.getCommitCnt());
+		verifyGraphObjectIndex();
+
+		assertCommitData("85b0176af27fa1640868f061f224d01e0b295f59",
+				new int[] { 5, 6 }, 1670570408L, 3, 0);
+		assertCommitData("d4f7c00aab3f0160168c9e5991abb6194a4e0d9e",
+				new int[] {}, 1670569901L, 1, 1);
+		assertCommitData("4d03aaf9c20c97d6ccdc05cb7f146b1deb6c01d5",
+				new int[] { 5 }, 1670570119L, 3, 2);
+		assertCommitData("a2f409b753880bf83b18bfb433dd340a6185e8be",
+				new int[] { 7 }, 1670569935L, 3, 3);
+		assertCommitData("431343847343979bbe31127ed905a24fed9a636c",
+				new int[] { 3, 2, 8 }, 1670570644L, 4, 4);
+		assertCommitData("c3f745ad8928ef56b5dbf33740fc8ede6b598290",
+				new int[] { 1 }, 1670570106L, 2, 5);
+		assertCommitData("95b12422c8ea4371e54cd58925eeed9d960ff1f0",
+				new int[] { 1 }, 1670570163L, 2, 6);
+		assertCommitData("de0ea882503cdd9c984c0a43238014569a123cac",
+				new int[] { 1 }, 1670569921L, 2, 7);
+		assertCommitData("102c9d6481559b1a113eb66bf55085903de6fb00",
+				new int[] { 6 }, 1670570616L, 3, 8);
+		assertCommitData("b5de2a84867f8ffc6321649dabf8c0680661ec03",
+				new int[] { 7, 5 }, 1670570364L, 3, 9);
+	}
+
+	private void verifyGraphObjectIndex() {
+		for (int i = 0; i < commitGraph.getCommitCnt(); i++) {
+			ObjectId id = commitGraph.getObjectId(i);
+			int pos = commitGraph.findGraphPosition(id);
+			assertEquals(i, pos);
+		}
+	}
+
+	private void assertCommitData(String expectedTree, int[] expectedParents,
+			long expectedCommitTime, int expectedGeneration, int graphPos) {
+		CommitData commitData = commitGraph.getCommitData(graphPos);
+		assertEquals(ObjectId.fromString(expectedTree), commitData.getTree());
+		assertArrayEquals(expectedParents, commitData.getParents());
+		assertEquals(expectedCommitTime, commitData.getCommitTime());
+		assertEquals(expectedGeneration, commitData.getGeneration());
+	}
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphTest.java
new file mode 100644
index 0000000..9797656
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphTest.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2022, Tencent.
+ *
+ * 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.commitgraph;
+
+import static org.eclipse.jgit.lib.Constants.COMMIT_GENERATION_UNKNOWN;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.jgit.internal.storage.file.FileRepository;
+import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Test writing and then reading the commit-graph.
+ */
+public class CommitGraphTest extends RepositoryTestCase {
+
+	private TestRepository<FileRepository> tr;
+
+	private CommitGraph commitGraph;
+
+	@Override
+	@Before
+	public void setUp() throws Exception {
+		super.setUp();
+		tr = new TestRepository<>(db, new RevWalk(db), mockSystemReader);
+	}
+
+	@Test
+	public void testGraphWithSingleCommit() throws Exception {
+		RevCommit root = commit();
+		writeAndReadCommitGraph(Collections.singleton(root));
+		verifyCommitGraph();
+		assertEquals(1, getGenerationNumber(root));
+	}
+
+	@Test
+	public void testGraphWithManyParents() throws Exception {
+		int parentsNum = 40;
+		RevCommit root = commit();
+
+		RevCommit[] parents = new RevCommit[parentsNum];
+		for (int i = 0; i < parents.length; i++) {
+			parents[i] = commit(root);
+		}
+		RevCommit tip = commit(parents);
+
+		Set<ObjectId> wants = Collections.singleton(tip);
+		writeAndReadCommitGraph(wants);
+		assertEquals(parentsNum + 2, commitGraph.getCommitCnt());
+		verifyCommitGraph();
+
+		assertEquals(1, getGenerationNumber(root));
+		for (RevCommit parent : parents) {
+			assertEquals(2, getGenerationNumber(parent));
+		}
+		assertEquals(3, getGenerationNumber(tip));
+	}
+
+	@Test
+	public void testGraphLinearHistory() throws Exception {
+		int commitNum = 20;
+		RevCommit[] commits = new RevCommit[commitNum];
+		for (int i = 0; i < commitNum; i++) {
+			if (i == 0) {
+				commits[i] = commit();
+			} else {
+				commits[i] = commit(commits[i - 1]);
+			}
+		}
+
+		Set<ObjectId> wants = Collections.singleton(commits[commitNum - 1]);
+		writeAndReadCommitGraph(wants);
+		assertEquals(commitNum, commitGraph.getCommitCnt());
+		verifyCommitGraph();
+		for (int i = 0; i < commitNum; i++) {
+			assertEquals(i + 1, getGenerationNumber(commits[i]));
+		}
+	}
+
+	@Test
+	public void testGraphWithMerges() throws Exception {
+		RevCommit c1 = commit();
+		RevCommit c2 = commit(c1);
+		RevCommit c3 = commit(c2);
+		RevCommit c4 = commit(c1);
+		RevCommit c5 = commit(c4);
+		RevCommit c6 = commit(c1);
+		RevCommit c7 = commit(c6);
+
+		RevCommit m1 = commit(c2, c4);
+		RevCommit m2 = commit(c4, c6);
+		RevCommit m3 = commit(c3, c5, c7);
+
+		Set<ObjectId> wants = new HashSet<>();
+
+		/*
+		 * <pre>
+		 * current graph structure:
+		 *    M1
+		 *   /  \
+		 *  2    4
+		 *  |___/
+		 *  1
+		 * </pre>
+		 */
+		wants.add(m1);
+		writeAndReadCommitGraph(wants);
+		assertEquals(4, commitGraph.getCommitCnt());
+		verifyCommitGraph();
+
+		/*
+		 * <pre>
+		 * current graph structure:
+		 *    M1   M2
+		 *   /  \ /  \
+		 *  2    4    6
+		 *  |___/____/
+		 *  1
+		 * </pre>
+		 */
+		wants.add(m2);
+		writeAndReadCommitGraph(wants);
+		assertEquals(6, commitGraph.getCommitCnt());
+		verifyCommitGraph();
+
+		/*
+		 * <pre>
+		 * current graph structure:
+		 *
+		 *    __M3___
+		 *   /   |   \
+		 *  3 M1 5 M2 7
+		 *  |/  \|/  \|
+		 *  2    4    6
+		 *  |___/____/
+		 *  1
+		 * </pre>
+		 */
+		wants.add(m3);
+		writeAndReadCommitGraph(wants);
+		assertEquals(10, commitGraph.getCommitCnt());
+		verifyCommitGraph();
+
+		/*
+		 * <pre>
+		 * current graph structure:
+		 *       8
+		 *       |
+		 *    __M3___
+		 *   /   |   \
+		 *  3 M1 5 M2 7
+		 *  |/  \|/  \|
+		 *  2    4    6
+		 *  |___/____/
+		 *  1
+		 * </pre>
+		 */
+		RevCommit c8 = commit(m3);
+		wants.add(c8);
+		writeAndReadCommitGraph(wants);
+		assertEquals(11, commitGraph.getCommitCnt());
+		verifyCommitGraph();
+
+		assertEquals(getGenerationNumber(c1), 1);
+		assertEquals(getGenerationNumber(c2), 2);
+		assertEquals(getGenerationNumber(c4), 2);
+		assertEquals(getGenerationNumber(c6), 2);
+		assertEquals(getGenerationNumber(c3), 3);
+		assertEquals(getGenerationNumber(c5), 3);
+		assertEquals(getGenerationNumber(c7), 3);
+		assertEquals(getGenerationNumber(m1), 3);
+		assertEquals(getGenerationNumber(m2), 3);
+		assertEquals(getGenerationNumber(m3), 4);
+		assertEquals(getGenerationNumber(c8), 5);
+	}
+
+	void writeAndReadCommitGraph(Set<ObjectId> wants) throws Exception {
+		NullProgressMonitor m = NullProgressMonitor.INSTANCE;
+		try (RevWalk walk = new RevWalk(db)) {
+			CommitGraphWriter writer = new CommitGraphWriter(
+					GraphCommits.fromWalk(m, wants, walk));
+			ByteArrayOutputStream os = new ByteArrayOutputStream();
+			writer.write(m, os);
+			InputStream inputStream = new ByteArrayInputStream(
+					os.toByteArray());
+			commitGraph = CommitGraphLoader.read(inputStream);
+		}
+	}
+
+	void verifyCommitGraph() throws Exception {
+		try (RevWalk walk = new RevWalk(db)) {
+			for (int i = 0; i < commitGraph.getCommitCnt(); i++) {
+				ObjectId objId = commitGraph.getObjectId(i);
+
+				// check the objectId index of commit-graph
+				int pos = commitGraph.findGraphPosition(objId);
+				assertEquals(i, pos);
+
+				// check the commit meta of commit-graph
+				CommitGraph.CommitData commit = commitGraph.getCommitData(i);
+				int[] pList = commit.getParents();
+
+				RevCommit expect = walk.lookupCommit(objId);
+				walk.parseBody(expect);
+
+				assertEquals(expect.getCommitTime(), commit.getCommitTime());
+				assertEquals(expect.getTree(), commit.getTree());
+				assertEquals(expect.getParentCount(), pList.length);
+
+				if (pList.length > 0) {
+					ObjectId[] parents = new ObjectId[pList.length];
+					for (int j = 0; j < parents.length; j++) {
+						parents[j] = commitGraph.getObjectId(pList[j]);
+					}
+					assertArrayEquals(expect.getParents(), parents);
+				}
+			}
+		}
+	}
+
+	int getGenerationNumber(ObjectId id) {
+		int graphPos = commitGraph.findGraphPosition(id);
+		CommitGraph.CommitData commitData = commitGraph.getCommitData(graphPos);
+		if (commitData != null) {
+			return commitData.getGeneration();
+		}
+		return COMMIT_GENERATION_UNKNOWN;
+	}
+
+	RevCommit commit(RevCommit... parents) throws Exception {
+		return tr.commit(parents);
+	}
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphWriterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphWriterTest.java
new file mode 100644
index 0000000..6c5e5e5
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphWriterTest.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2021, Tencent.
+ *
+ * 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.commitgraph;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayOutputStream;
+import java.util.Collections;
+import java.util.Set;
+
+import org.eclipse.jgit.internal.storage.file.FileRepository;
+import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.util.NB;
+import org.junit.Before;
+import org.junit.Test;
+
+public class CommitGraphWriterTest extends RepositoryTestCase {
+
+	private TestRepository<FileRepository> tr;
+
+	private ByteArrayOutputStream os;
+
+	private CommitGraphWriter writer;
+
+	private RevWalk walk;
+
+	@Override
+	@Before
+	public void setUp() throws Exception {
+		super.setUp();
+		os = new ByteArrayOutputStream();
+		tr = new TestRepository<>(db, new RevWalk(db), mockSystemReader);
+		walk = new RevWalk(db);
+	}
+
+	@Test
+	public void testWriteInEmptyRepo() throws Exception {
+		NullProgressMonitor m = NullProgressMonitor.INSTANCE;
+		writer = new CommitGraphWriter(
+				GraphCommits.fromWalk(m, Collections.emptySet(), walk));
+		writer.write(m, os);
+		assertEquals(0, os.size());
+	}
+
+	@Test
+	public void testWriterWithExtraEdgeList() throws Exception {
+		RevCommit root = commit();
+		RevCommit a = commit(root);
+		RevCommit b = commit(root);
+		RevCommit c = commit(root);
+		RevCommit tip = commit(a, b, c);
+
+		Set<ObjectId> wants = Collections.singleton(tip);
+		NullProgressMonitor m = NullProgressMonitor.INSTANCE;
+		GraphCommits graphCommits = GraphCommits.fromWalk(m, wants, walk);
+		writer = new CommitGraphWriter(graphCommits);
+		writer.write(m, os);
+
+		assertEquals(5, graphCommits.size());
+		byte[] data = os.toByteArray();
+		assertTrue(data.length > 0);
+		byte[] headers = new byte[8];
+		System.arraycopy(data, 0, headers, 0, 8);
+		assertArrayEquals(new byte[] {'C', 'G', 'P', 'H', 1, 1, 4, 0}, headers);
+		assertEquals(CommitGraphConstants.CHUNK_ID_OID_FANOUT, NB.decodeInt32(data, 8));
+		assertEquals(CommitGraphConstants.CHUNK_ID_OID_LOOKUP, NB.decodeInt32(data, 20));
+		assertEquals(CommitGraphConstants.CHUNK_ID_COMMIT_DATA, NB.decodeInt32(data, 32));
+		assertEquals(CommitGraphConstants.CHUNK_ID_EXTRA_EDGE_LIST, NB.decodeInt32(data, 44));
+	}
+
+	@Test
+	public void testWriterWithoutExtraEdgeList() throws Exception {
+		RevCommit root = commit();
+		RevCommit a = commit(root);
+		RevCommit b = commit(root);
+		RevCommit tip = commit(a, b);
+
+		Set<ObjectId> wants = Collections.singleton(tip);
+		NullProgressMonitor m = NullProgressMonitor.INSTANCE;
+		GraphCommits graphCommits = GraphCommits.fromWalk(m, wants, walk);
+		writer = new CommitGraphWriter(graphCommits);
+		writer.write(m, os);
+
+		assertEquals(4, graphCommits.size());
+		byte[] data = os.toByteArray();
+		assertTrue(data.length > 0);
+		byte[] headers = new byte[8];
+		System.arraycopy(data, 0, headers, 0, 8);
+		assertArrayEquals(new byte[] {'C', 'G', 'P', 'H', 1, 1, 3, 0}, headers);
+		assertEquals(CommitGraphConstants.CHUNK_ID_OID_FANOUT, NB.decodeInt32(data, 8));
+		assertEquals(CommitGraphConstants.CHUNK_ID_OID_LOOKUP, NB.decodeInt32(data, 20));
+		assertEquals(CommitGraphConstants.CHUNK_ID_COMMIT_DATA, NB.decodeInt32(data, 32));
+	}
+
+	RevCommit commit(RevCommit... parents) throws Exception {
+		return tr.commit(parents);
+	}
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/commitgraph/GraphCommitsTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/commitgraph/GraphCommitsTest.java
new file mode 100644
index 0000000..fc05feb
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/commitgraph/GraphCommitsTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2021, Tencent.
+ *
+ * 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.commitgraph;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThrows;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.internal.storage.file.FileRepository;
+import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.junit.Before;
+import org.junit.Test;
+
+public class GraphCommitsTest extends RepositoryTestCase {
+
+	private TestRepository<FileRepository> tr;
+
+	@Override
+	@Before
+	public void setUp() throws Exception {
+		super.setUp();
+		tr = new TestRepository<>(db, new RevWalk(db), mockSystemReader);
+	}
+
+	@Test
+	public void testEmptyRepo() throws Exception {
+		try (RevWalk rw = new RevWalk(db)) {
+			GraphCommits commits = GraphCommits.fromWalk(
+					NullProgressMonitor.INSTANCE, Collections.emptySet(), rw);
+			assertEquals(0, commits.size());
+			assertEquals(0, commits.getExtraEdgeCnt());
+			assertFalse(commits.iterator().hasNext());
+		}
+	}
+
+	@Test
+	public void testRepoWithoutExtraEdges() throws Exception {
+		try (RevWalk rw = new RevWalk(db)) {
+			RevCommit root = commit();
+			RevCommit a = commit(root);
+			RevCommit b = commit(root);
+			RevCommit tip = commit(a, b);
+			GraphCommits commits = GraphCommits.fromWalk(
+					NullProgressMonitor.INSTANCE, Collections.singleton(tip),
+					rw);
+			assertEquals(4, commits.size());
+			assertEquals(0, commits.getExtraEdgeCnt());
+			verifyOrderOfLookup(List.of(root, a, b, tip), commits);
+		}
+	}
+
+	@Test
+	public void testRepoWithExtraEdges() throws Exception {
+		try (RevWalk rw = new RevWalk(db)) {
+			RevCommit root = commit();
+			RevCommit a = commit(root);
+			RevCommit b = commit(root);
+			RevCommit c = commit(root);
+			RevCommit tip = commit(a, b, c);
+			GraphCommits commits = GraphCommits.fromWalk(
+					NullProgressMonitor.INSTANCE, Collections.singleton(tip),
+					rw);
+			assertEquals(5, commits.size());
+			assertEquals(2, commits.getExtraEdgeCnt());
+			verifyOrderOfLookup(List.of(root, a, b, c, tip), commits);
+		}
+	}
+
+	@Test
+	public void testCommitNotInLookup() throws Exception {
+		try (RevWalk rw = new RevWalk(db)) {
+			RevCommit a = commit();
+			RevCommit b = commit(a);
+			GraphCommits commits = GraphCommits.fromWalk(
+					NullProgressMonitor.INSTANCE, Collections.singleton(a), rw);
+			assertEquals(1, commits.size());
+			assertEquals(0, commits.getExtraEdgeCnt());
+			Iterator<RevCommit> iterator = commits.iterator();
+			assertEquals(a, iterator.next());
+			assertFalse(iterator.hasNext());
+			assertThrows(MissingObjectException.class,
+					() -> commits.getOidPosition(b));
+		}
+	}
+
+	private void verifyOrderOfLookup(List<RevCommit> commits,
+			GraphCommits graphCommits) {
+		commits = new ArrayList<>(commits);
+		Collections.sort(commits);
+		Iterator<RevCommit> expected = commits.iterator();
+		Iterator<RevCommit> actual = graphCommits.iterator();
+		while (expected.hasNext()) {
+			assertEquals(expected.next(), actual.next());
+		}
+		assertFalse(actual.hasNext());
+	}
+
+	RevCommit commit(RevCommit... parents) throws Exception {
+		return tr.commit(parents);
+	}
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheTest.java
index 3dd4190..fef0563 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheTest.java
@@ -284,12 +284,14 @@
 			asyncRun(() -> pack.getBitmapIndex(reader));
 			asyncRun(() -> pack.getPackIndex(reader));
 			asyncRun(() -> pack.getBitmapIndex(reader));
+			asyncRun(() -> pack.getCommitGraph(reader));
 		}
 		waitForExecutorPoolTermination();
 
 		assertEquals(1, cache.getMissCount()[PackExt.BITMAP_INDEX.ordinal()]);
 		assertEquals(1, cache.getMissCount()[PackExt.INDEX.ordinal()]);
 		assertEquals(1, cache.getMissCount()[PackExt.REVERSE_INDEX.ordinal()]);
+		assertEquals(1, cache.getMissCount()[PackExt.COMMIT_GRAPH.ordinal()]);
 	}
 
 	@SuppressWarnings("resource")
@@ -313,12 +315,15 @@
 			}
 			asyncRun(() -> pack1.getBitmapIndex(reader));
 			asyncRun(() -> pack2.getBitmapIndex(reader));
+			asyncRun(() -> pack1.getCommitGraph(reader));
+			asyncRun(() -> pack2.getCommitGraph(reader));
 		}
 
 		waitForExecutorPoolTermination();
 		assertEquals(2, cache.getMissCount()[PackExt.BITMAP_INDEX.ordinal()]);
 		assertEquals(2, cache.getMissCount()[PackExt.INDEX.ordinal()]);
 		assertEquals(2, cache.getMissCount()[PackExt.REVERSE_INDEX.ordinal()]);
+		assertEquals(2, cache.getMissCount()[PackExt.COMMIT_GRAPH.ordinal()]);
 	}
 
 	@SuppressWarnings("resource")
@@ -342,12 +347,15 @@
 			}
 			asyncRun(() -> pack1.getBitmapIndex(reader));
 			asyncRun(() -> pack2.getBitmapIndex(reader));
+			asyncRun(() -> pack1.getCommitGraph(reader));
+			asyncRun(() -> pack2.getCommitGraph(reader));
 		}
 
 		waitForExecutorPoolTermination();
 		assertEquals(2, cache.getMissCount()[PackExt.BITMAP_INDEX.ordinal()]);
 		assertEquals(2, cache.getMissCount()[PackExt.INDEX.ordinal()]);
 		assertEquals(2, cache.getMissCount()[PackExt.REVERSE_INDEX.ordinal()]);
+		assertEquals(2, cache.getMissCount()[PackExt.COMMIT_GRAPH.ordinal()]);
 	}
 
 	@SuppressWarnings("resource")
@@ -372,7 +380,9 @@
 			}
 			asyncRun(() -> pack1.getBitmapIndex(reader));
 			asyncRun(() -> pack1.getPackIndex(reader));
+			asyncRun(() -> pack1.getCommitGraph(reader));
 			asyncRun(() -> pack2.getBitmapIndex(reader));
+			asyncRun(() -> pack2.getCommitGraph(reader));
 		}
 		waitForExecutorPoolTermination();
 
@@ -380,6 +390,7 @@
 		// Index is loaded once for each repo.
 		assertEquals(2, cache.getMissCount()[PackExt.INDEX.ordinal()]);
 		assertEquals(2, cache.getMissCount()[PackExt.REVERSE_INDEX.ordinal()]);
+		assertEquals(2, cache.getMissCount()[PackExt.COMMIT_GRAPH.ordinal()]);
 	}
 
 	@Test
@@ -396,12 +407,14 @@
 			asyncRun(() -> pack.getBitmapIndex(reader));
 			asyncRun(() -> pack.getPackIndex(reader));
 			asyncRun(() -> pack.getBitmapIndex(reader));
+			asyncRun(() -> pack.getCommitGraph(reader));
 		}
 		waitForExecutorPoolTermination();
 
 		assertEquals(1, cache.getMissCount()[PackExt.BITMAP_INDEX.ordinal()]);
 		assertEquals(1, cache.getMissCount()[PackExt.INDEX.ordinal()]);
 		assertEquals(1, cache.getMissCount()[PackExt.REVERSE_INDEX.ordinal()]);
+		assertEquals(1, cache.getMissCount()[PackExt.COMMIT_GRAPH.ordinal()]);
 	}
 
 	@Test
@@ -420,12 +433,14 @@
 			asyncRun(() -> pack.getBitmapIndex(reader));
 			asyncRun(() -> pack.getPackIndex(reader));
 			asyncRun(() -> pack.getBitmapIndex(reader));
+			asyncRun(() -> pack.getCommitGraph(reader));
 		}
 		waitForExecutorPoolTermination();
 
 		assertEquals(1, cache.getMissCount()[PackExt.BITMAP_INDEX.ordinal()]);
 		assertEquals(1, cache.getMissCount()[PackExt.INDEX.ordinal()]);
 		assertEquals(1, cache.getMissCount()[PackExt.REVERSE_INDEX.ordinal()]);
+		assertEquals(1, cache.getMissCount()[PackExt.COMMIT_GRAPH.ordinal()]);
 	}
 
 	private void resetCache() {
@@ -450,7 +465,7 @@
 			repository.branch("/refs/ref2" + repoName).commit()
 					.add("blob2", "blob2" + repoName).parent(commit).create();
 		}
-		new DfsGarbageCollector(repo).pack(null);
+		new DfsGarbageCollector(repo).setWriteCommitGraph(true).pack(null);
 		return repo;
 	}
 
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollectorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollectorTest.java
index f235b2e..ab99895 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollectorTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollectorTest.java
@@ -18,6 +18,7 @@
 import java.util.Collections;
 import java.util.concurrent.TimeUnit;
 
+import org.eclipse.jgit.internal.storage.commitgraph.CommitGraph;
 import org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource;
 import org.eclipse.jgit.internal.storage.reftable.RefCursor;
 import org.eclipse.jgit.internal.storage.reftable.ReftableConfig;
@@ -976,10 +977,139 @@
 		assertNull(refdb.exactRef(NEXT));
 	}
 
+	@Test
+	public void produceCommitGraphAllRefsIncludedFromDisk() throws Exception {
+		String tag = "refs/tags/tag1";
+		String head = "refs/heads/head1";
+		String nonHead = "refs/something/nonHead";
+
+		RevCommit rootCommitTagged = git.branch(tag).commit().message("0")
+				.noParents().create();
+		RevCommit headTip = git.branch(head).commit().message("1")
+				.parent(rootCommitTagged).create();
+		RevCommit nonHeadTip = git.branch(nonHead).commit().message("2")
+				.parent(rootCommitTagged).create();
+
+		gcWithCommitGraph();
+
+		assertEquals(2, odb.getPacks().length);
+		DfsPackFile gcPack = odb.getPacks()[0];
+		assertEquals(GC, gcPack.getPackDescription().getPackSource());
+
+		DfsReader reader = odb.newReader();
+		CommitGraph cg = gcPack.getCommitGraph(reader);
+		assertNotNull(cg);
+
+		assertTrue("all commits in commit graph", cg.getCommitCnt() == 3);
+		// GC packed
+		assertTrue("tag referenced commit is in graph",
+				cg.findGraphPosition(rootCommitTagged) != -1);
+		assertTrue("head referenced commit is in graph",
+				cg.findGraphPosition(headTip) != -1);
+		// GC_REST packed
+		assertTrue("nonHead referenced commit is in graph",
+				cg.findGraphPosition(nonHeadTip) != -1);
+	}
+
+	@Test
+	public void produceCommitGraphAllRefsIncludedFromCache() throws Exception {
+		String tag = "refs/tags/tag1";
+		String head = "refs/heads/head1";
+		String nonHead = "refs/something/nonHead";
+
+		RevCommit rootCommitTagged = git.branch(tag).commit().message("0")
+				.noParents().create();
+		RevCommit headTip = git.branch(head).commit().message("1")
+				.parent(rootCommitTagged).create();
+		RevCommit nonHeadTip = git.branch(nonHead).commit().message("2")
+				.parent(rootCommitTagged).create();
+
+		gcWithCommitGraph();
+
+		assertEquals(2, odb.getPacks().length);
+		DfsPackFile gcPack = odb.getPacks()[0];
+		assertEquals(GC, gcPack.getPackDescription().getPackSource());
+
+		DfsReader reader = odb.newReader();
+		gcPack.getCommitGraph(reader);
+		// Invoke cache hit
+		CommitGraph cachedCG = gcPack.getCommitGraph(reader);
+		assertNotNull(cachedCG);
+		assertTrue("commit graph have been read from disk once",
+				reader.stats.readCommitGraph == 1);
+		assertTrue("commit graph read contains content",
+				reader.stats.readCommitGraphBytes > 0);
+		assertTrue("commit graph read time is recorded",
+				reader.stats.readCommitGraphMicros > 0);
+
+		assertTrue("all commits in commit graph", cachedCG.getCommitCnt() == 3);
+		// GC packed
+		assertTrue("tag referenced commit is in graph",
+				cachedCG.findGraphPosition(rootCommitTagged) != -1);
+		assertTrue("head referenced commit is in graph",
+				cachedCG.findGraphPosition(headTip) != -1);
+		// GC_REST packed
+		assertTrue("nonHead referenced commit is in graph",
+				cachedCG.findGraphPosition(nonHeadTip) != -1);
+	}
+
+	@Test
+	public void noCommitGraphWithoutGcPack() throws Exception {
+		String nonHead = "refs/something/nonHead";
+		RevCommit nonHeadCommit = git.branch(nonHead).commit()
+				.message("nonhead").noParents().create();
+		commit().message("unreachable").parent(nonHeadCommit).create();
+
+		gcWithCommitGraph();
+
+		assertEquals(2, odb.getPacks().length);
+		for (DfsPackFile pack : odb.getPacks()) {
+			assertNull(pack.getCommitGraph(odb.newReader()));
+		}
+	}
+
+	@Test
+	public void commitGraphWithoutGCrestPack() throws Exception {
+		String head = "refs/heads/head1";
+		RevCommit headCommit = git.branch(head).commit().message("head")
+				.noParents().create();
+		RevCommit unreachableCommit = commit().message("unreachable")
+				.parent(headCommit).create();
+
+		gcWithCommitGraph();
+
+		assertEquals(2, odb.getPacks().length);
+		for (DfsPackFile pack : odb.getPacks()) {
+			DfsPackDescription d = pack.getPackDescription();
+			if (d.getPackSource() == GC) {
+				CommitGraph cg = pack.getCommitGraph(odb.newReader());
+				assertNotNull(cg);
+				assertTrue("commit graph only contains 1 commit",
+						cg.getCommitCnt() == 1);
+				assertTrue("head exists in commit graph",
+						cg.findGraphPosition(headCommit) != -1);
+				assertTrue("unreachable commit does not exist in commit graph",
+						cg.findGraphPosition(unreachableCommit) == -1);
+			} else if (d.getPackSource() == UNREACHABLE_GARBAGE) {
+				CommitGraph cg = pack.getCommitGraph(odb.newReader());
+				assertNull(cg);
+			} else {
+				fail("unexpected " + d.getPackSource());
+				break;
+			}
+		}
+	}
+
 	private TestRepository<InMemoryRepository>.CommitBuilder commit() {
 		return git.commit();
 	}
 
+	private void gcWithCommitGraph() throws IOException {
+		DfsGarbageCollector gc = new DfsGarbageCollector(repo);
+		gc.setWriteCommitGraph(true);
+		run(gc);
+	}
+
 	private void gcNoTtl() throws IOException {
 		DfsGarbageCollector gc = new DfsGarbageCollector(repo);
 		gc.setGarbageTtl(0, TimeUnit.MILLISECONDS); // disable TTL
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcCommitGraphTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcCommitGraphTest.java
new file mode 100644
index 0000000..dbc9dba
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcCommitGraphTest.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2022, Tencent.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.eclipse.jgit.internal.storage.file;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.util.Collections;
+
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.ConfigConstants;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.StoredConfig;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.util.IO;
+import org.junit.Test;
+
+public class GcCommitGraphTest extends GcTestCase {
+
+	@Test
+	public void testCommitGraphConfig() {
+		StoredConfig config = repo.getConfig();
+		assertFalse(gc.shouldWriteCommitGraphWhenGc());
+
+		config.setBoolean(ConfigConstants.CONFIG_GC_SECTION, null,
+				ConfigConstants.CONFIG_KEY_WRITE_COMMIT_GRAPH, true);
+		assertTrue(gc.shouldWriteCommitGraphWhenGc());
+
+		config.setBoolean(ConfigConstants.CONFIG_GC_SECTION, null,
+				ConfigConstants.CONFIG_KEY_WRITE_COMMIT_GRAPH, false);
+		assertFalse(gc.shouldWriteCommitGraphWhenGc());
+	}
+
+	@Test
+	public void testWriteEmptyRepo() throws Exception {
+		StoredConfig config = repo.getConfig();
+		config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
+				ConfigConstants.CONFIG_COMMIT_GRAPH, true);
+		config.setBoolean(ConfigConstants.CONFIG_GC_SECTION, null,
+				ConfigConstants.CONFIG_KEY_WRITE_COMMIT_GRAPH, true);
+
+		assertTrue(gc.shouldWriteCommitGraphWhenGc());
+		gc.writeCommitGraph(Collections.emptySet());
+		File graphFile = new File(repo.getObjectsDirectory(),
+				Constants.INFO_COMMIT_GRAPH);
+		assertFalse(graphFile.exists());
+	}
+
+	@Test
+	public void testWriteShallowRepo() throws Exception {
+		StoredConfig config = repo.getConfig();
+		config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
+				ConfigConstants.CONFIG_COMMIT_GRAPH, true);
+		config.setBoolean(ConfigConstants.CONFIG_GC_SECTION, null,
+				ConfigConstants.CONFIG_KEY_WRITE_COMMIT_GRAPH, true);
+
+		RevCommit tip = commitChain(2);
+		TestRepository.BranchBuilder bb = tr.branch("refs/heads/master");
+		bb.update(tip);
+		repo.getObjectDatabase().setShallowCommits(Collections.singleton(tip));
+
+		gc.writeCommitGraph(Collections.singleton(tip));
+		File graphFile = new File(repo.getObjectsDirectory(),
+				Constants.INFO_COMMIT_GRAPH);
+		assertFalse(graphFile.exists());
+	}
+
+	@Test
+	public void testWriteWhenGc() throws Exception {
+		StoredConfig config = repo.getConfig();
+		config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
+				ConfigConstants.CONFIG_COMMIT_GRAPH, true);
+		config.setBoolean(ConfigConstants.CONFIG_GC_SECTION, null,
+				ConfigConstants.CONFIG_KEY_WRITE_COMMIT_GRAPH, true);
+
+		RevCommit tip = commitChain(10);
+		TestRepository.BranchBuilder bb = tr.branch("refs/heads/master");
+		bb.update(tip);
+
+		assertTrue(gc.shouldWriteCommitGraphWhenGc());
+		gc.gc().get();
+		File graphFile = new File(repo.getObjectsDirectory(),
+				Constants.INFO_COMMIT_GRAPH);
+		assertGraphFile(graphFile);
+	}
+
+	@Test
+	public void testDefaultWriteWhenGc() throws Exception {
+		RevCommit tip = commitChain(10);
+		TestRepository.BranchBuilder bb = tr.branch("refs/heads/master");
+		bb.update(tip);
+
+		assertFalse(gc.shouldWriteCommitGraphWhenGc());
+		gc.gc().get();
+		File graphFile = new File(repo.getObjectsDirectory(),
+				Constants.INFO_COMMIT_GRAPH);
+		assertFalse(graphFile.exists());
+	}
+
+	@Test
+	public void testDisableWriteWhenGc() throws Exception {
+		RevCommit tip = commitChain(10);
+		TestRepository.BranchBuilder bb = tr.branch("refs/heads/master");
+		bb.update(tip);
+		File graphFile = new File(repo.getObjectsDirectory(),
+				Constants.INFO_COMMIT_GRAPH);
+
+		StoredConfig config = repo.getConfig();
+		config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
+				ConfigConstants.CONFIG_COMMIT_GRAPH, false);
+		config.setBoolean(ConfigConstants.CONFIG_GC_SECTION, null,
+				ConfigConstants.CONFIG_KEY_WRITE_COMMIT_GRAPH, true);
+
+		gc.gc().get();
+		assertFalse(graphFile.exists());
+
+		config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
+				ConfigConstants.CONFIG_COMMIT_GRAPH, true);
+		config.setBoolean(ConfigConstants.CONFIG_GC_SECTION, null,
+				ConfigConstants.CONFIG_KEY_WRITE_COMMIT_GRAPH, false);
+		gc.gc().get();
+		assertFalse(graphFile.exists());
+
+		config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
+				ConfigConstants.CONFIG_COMMIT_GRAPH, false);
+		config.setBoolean(ConfigConstants.CONFIG_GC_SECTION, null,
+				ConfigConstants.CONFIG_KEY_WRITE_COMMIT_GRAPH, false);
+		gc.gc().get();
+		assertFalse(graphFile.exists());
+	}
+
+	@Test
+	public void testWriteCommitGraphOnly() throws Exception {
+		RevCommit tip = commitChain(10);
+		TestRepository.BranchBuilder bb = tr.branch("refs/heads/master");
+		bb.update(tip);
+
+		StoredConfig config = repo.getConfig();
+		config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
+				ConfigConstants.CONFIG_COMMIT_GRAPH, false);
+		gc.writeCommitGraph(Collections.singleton(tip));
+
+		File graphFile = new File(repo.getObjectsDirectory(),
+				Constants.INFO_COMMIT_GRAPH);
+		assertFalse(graphFile.exists());
+
+		config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
+				ConfigConstants.CONFIG_COMMIT_GRAPH, true);
+		gc.writeCommitGraph(Collections.singleton(tip));
+		assertGraphFile(graphFile);
+	}
+
+	private void assertGraphFile(File graphFile) throws Exception {
+		assertTrue(graphFile.exists());
+		try (InputStream os = new FileInputStream(graphFile)) {
+			byte[] magic = new byte[4];
+			IO.readFully(os, magic, 0, 4);
+			assertArrayEquals(new byte[] { 'C', 'G', 'P', 'H' }, magic);
+		}
+	}
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcOrphanFilesTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcOrphanFilesTest.java
index 620aedf..342e559 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcOrphanFilesTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcOrphanFilesTest.java
@@ -10,6 +10,7 @@
 
 package org.eclipse.jgit.internal.storage.file;
 
+import static org.eclipse.jgit.internal.storage.pack.PackExt.REVERSE_INDEX;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
@@ -36,6 +37,14 @@
 
 	private static final String PACK_File_3 = PACK + "-3.pack";
 
+	private static final String REVERSE_File_2 = PACK + "-2."
+			+ REVERSE_INDEX.getExtension();
+
+	private static final String REVERSE_File_4 = PACK + "-4."
+			+ REVERSE_INDEX.getExtension();
+
+	private static final String NONEXISTENT_EXT = PACK + "-4.xxxxx";
+
 	private File packDir;
 
 	@Override
@@ -46,14 +55,27 @@
 	}
 
 	@Test
-	public void bitmapAndIdxDeletedButPackNot() throws Exception {
+	public void indexesDeletedButPackNot() throws Exception {
 		createFileInPackFolder(BITMAP_File_1);
 		createFileInPackFolder(IDX_File_2);
 		createFileInPackFolder(PACK_File_3);
+		createFileInPackFolder(REVERSE_File_4);
 		gc.gc().get();
 		assertFalse(new File(packDir, BITMAP_File_1).exists());
 		assertFalse(new File(packDir, IDX_File_2).exists());
 		assertTrue(new File(packDir, PACK_File_3).exists());
+		assertFalse(new File(packDir, REVERSE_File_4).exists());
+	}
+
+	@Test
+	public void noPacks_allIndexesDeleted() throws Exception {
+		createFileInPackFolder(BITMAP_File_1);
+		createFileInPackFolder(IDX_File_2);
+		createFileInPackFolder(REVERSE_File_4);
+		gc.gc().get();
+		assertFalse(new File(packDir, BITMAP_File_1).exists());
+		assertFalse(new File(packDir, IDX_File_2).exists());
+		assertFalse(new File(packDir, REVERSE_File_4).exists());
 	}
 
 	@Test
@@ -77,18 +99,27 @@
 	}
 
 	@Test
+	public void nonexistantExtensionNotDeleted() throws Exception {
+		createFileInPackFolder(NONEXISTENT_EXT);
+		gc.gc().get();
+		assertTrue(new File(packDir, NONEXISTENT_EXT).exists());
+	}
+
+	@Test
 	public void keepPreventsDeletionOfIndexFilesForMissingPackFile()
 			throws Exception {
 		createFileInPackFolder(BITMAP_File_1);
-		createFileInPackFolder(IDX_File_2);
 		createFileInPackFolder(BITMAP_File_2);
+		createFileInPackFolder(IDX_File_2);
 		createFileInPackFolder(KEEP_File_2);
+		createFileInPackFolder(REVERSE_File_2);
 		createFileInPackFolder(PACK_File_3);
 		gc.gc().get();
 		assertFalse(new File(packDir, BITMAP_File_1).exists());
 		assertTrue(new File(packDir, BITMAP_File_2).exists());
 		assertTrue(new File(packDir, IDX_File_2).exists());
 		assertTrue(new File(packDir, KEEP_File_2).exists());
+		assertTrue(new File(packDir, REVERSE_File_2).exists());
 		assertTrue(new File(packDir, PACK_File_3).exists());
 	}
 
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ObjectDirectoryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ObjectDirectoryTest.java
index 1a3b378..746a0a1 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ObjectDirectoryTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ObjectDirectoryTest.java
@@ -43,6 +43,7 @@
 package org.eclipse.jgit.internal.storage.file;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertThrows;
@@ -236,6 +237,26 @@
 	}
 
 	@Test
+	public void testWindowCursorGetCommitGraph() throws Exception {
+		db.getConfig().setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
+				ConfigConstants.CONFIG_COMMIT_GRAPH, true);
+		db.getConfig().setBoolean(ConfigConstants.CONFIG_GC_SECTION, null,
+				ConfigConstants.CONFIG_KEY_WRITE_COMMIT_GRAPH, true);
+
+		WindowCursor curs = new WindowCursor(db.getObjectDatabase());
+		assertTrue(curs.getCommitGraph().isEmpty());
+		commitFile("file.txt", "content", "master");
+		GC gc = new GC(db);
+		gc.gc().get();
+		assertTrue(curs.getCommitGraph().isPresent());
+
+		db.getConfig().setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
+				ConfigConstants.CONFIG_COMMIT_GRAPH, false);
+
+		assertTrue(curs.getCommitGraph().isEmpty());
+	}
+
+	@Test
 	public void testShallowFileCorrupt() throws Exception {
 		FileRepository repository = createBareRepository();
 		ObjectDirectory dir = repository.getObjectDatabase();
@@ -251,6 +272,58 @@
 				IOException.class, () -> dir.getShallowCommits());
 	}
 
+	@Test
+	public void testGetCommitGraph() throws Exception {
+		db.getConfig().setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
+				ConfigConstants.CONFIG_COMMIT_GRAPH, true);
+		db.getConfig().setBoolean(ConfigConstants.CONFIG_GC_SECTION, null,
+				ConfigConstants.CONFIG_KEY_WRITE_COMMIT_GRAPH, true);
+		db.getConfig().save();
+
+		// no commit-graph
+		ObjectDirectory dir = db.getObjectDatabase();
+		assertTrue(dir.getCommitGraph().isEmpty());
+
+		// add commit-graph
+		commitFile("file.txt", "content", "master");
+		GC gc = new GC(db);
+		gc.gc().get();
+		File file = new File(db.getObjectsDirectory(),
+				Constants.INFO_COMMIT_GRAPH);
+		assertTrue(file.exists());
+		assertTrue(file.isFile());
+		assertTrue(dir.getCommitGraph().isPresent());
+		assertEquals(1, dir.getCommitGraph().get().getCommitCnt());
+
+		// get commit-graph in a newly created db
+		try (FileRepository repo2 = new FileRepository(db.getDirectory())) {
+			ObjectDirectory dir2 = repo2.getObjectDatabase();
+			assertTrue(dir2.getCommitGraph().isPresent());
+			assertEquals(1, dir2.getCommitGraph().get().getCommitCnt());
+		}
+
+		// update commit-graph
+		commitFile("file2.txt", "content", "master");
+		gc.gc().get();
+		assertEquals(2, dir.getCommitGraph().get().getCommitCnt());
+
+		// delete commit-graph
+		file.delete();
+		assertFalse(file.exists());
+		assertTrue(dir.getCommitGraph().isEmpty());
+
+		// commit-graph is corrupt
+		try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) {
+			writer.println("this is a corrupt commit-graph");
+		}
+		assertTrue(dir.getCommitGraph().isEmpty());
+
+		// add commit-graph again
+		gc.gc().get();
+		assertTrue(dir.getCommitGraph().isPresent());
+		assertEquals(2, dir.getCommitGraph().get().getCommitCnt());
+	}
+
 	private Collection<Callable<ObjectId>> blobInsertersForTheSameFanOutDir(
 			final ObjectDirectory dir) {
 		Callable<ObjectId> callable = () -> dir.newInserter()
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackIndexTestCase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackIndexTestCase.java
index 910b928..67bba18 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackIndexTestCase.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackIndexTestCase.java
@@ -25,6 +25,7 @@
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.internal.storage.file.PackIndex.MutableEntry;
 import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.lib.ObjectId;
 import org.junit.Test;
 
 public abstract class PackIndexTestCase extends RepositoryTestCase {
@@ -122,6 +123,37 @@
 		assertFalse(iter.hasNext());
 	}
 
+	@Test
+	public void testEntriesPositionsRamdomAccess() {
+		assertEquals(4, smallIdx.findPosition(ObjectId
+				.fromString("82c6b885ff600be425b4ea96dee75dca255b69e7")));
+		assertEquals(7, smallIdx.findPosition(ObjectId
+				.fromString("c59759f143fb1fe21c197981df75a7ee00290799")));
+		assertEquals(0, smallIdx.findPosition(ObjectId
+				.fromString("4b825dc642cb6eb9a060e54bf8d69288fbee4904")));
+	}
+
+	@Test
+	public void testEntriesPositionsWithIteratorOrder() {
+		int i = 0;
+		for (MutableEntry me : smallIdx) {
+			assertEquals(smallIdx.findPosition(me.toObjectId()), i);
+			i++;
+		}
+		i = 0;
+		for (MutableEntry me : denseIdx) {
+			assertEquals(denseIdx.findPosition(me.toObjectId()), i);
+			i++;
+		}
+	}
+
+	@Test
+	public void testEntriesPositionsObjectNotInPack() {
+		assertEquals(-1, smallIdx.findPosition(ObjectId.zeroId()));
+		assertEquals(-1, smallIdx.findPosition(ObjectId
+				.fromString("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")));
+	}
+
 	/**
 	 * Compare offset from iterator entries with output of findOffset() method.
 	 */
@@ -135,6 +167,13 @@
 		}
 	}
 
+	@Test
+	public void testEntriesOffsetsObjectNotInPack() {
+		assertEquals(-1, smallIdx.findOffset(ObjectId.zeroId()));
+		assertEquals(-1, smallIdx.findOffset(ObjectId
+				.fromString("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")));
+	}
+
 	/**
 	 * Compare offset from iterator entries with output of getOffset() method.
 	 */
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackObjectSizeIndexV1Test.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackObjectSizeIndexV1Test.java
new file mode 100644
index 0000000..11e3746
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackObjectSizeIndexV1Test.java
@@ -0,0 +1,392 @@
+/*
+ * Copyright (C) 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
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.storage.file;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.transport.PackedObjectInfo;
+import org.eclipse.jgit.util.BlockList;
+import org.junit.Test;
+
+public class PackObjectSizeIndexV1Test {
+	private static final ObjectId OBJ_ID = ObjectId
+			.fromString("b8b1d53172fb3fb19647adce4b38fab4371c2454");
+
+	private static final long GB = 1 << 30;
+
+	private static final int MAX_24BITS_UINT = 0xffffff;
+
+	@Test
+	public void write_24bPositions_32bSizes() throws IOException {
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		PackObjectSizeIndexWriter writer = PackObjectSizeIndexWriter
+				.createWriter(out, 0);
+		List<PackedObjectInfo> objs = new ArrayList<>();
+		objs.add(blobWithSize(100));
+		objs.add(blobWithSize(400));
+		objs.add(blobWithSize(200));
+
+		writer.write(objs);
+		byte[] expected = new byte[] { -1, 's', 'i', 'z', // header
+				0x01, // version
+				0x00, 0x00, 0x00, 0x00, // minimum object size
+				0x00, 0x00, 0x00, 0x03, // obj count
+				0x18, // Unsigned 3 bytes
+				0x00, 0x00, 0x00, 0x03, // 3 positions
+				0x00, 0x00, 0x00, // positions
+				0x00, 0x00, 0x01, //
+				0x00, 0x00, 0x02, //
+				0x00, // No more positions
+				0x00, 0x00, 0x00, 0x64, // size 100
+				0x00, 0x00, 0x01, (byte) 0x90, // size 400
+				0x00, 0x00, 0x00, (byte) 0xc8, // size 200
+				0x00, 0x00, 0x00, 0x00 // 64bit sizes counter
+		};
+		byte[] output = out.toByteArray();
+		assertArrayEquals(expected, output);
+	}
+
+	@Test
+	public void write_32bPositions_32bSizes() throws IOException {
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		PackObjectSizeIndexWriter writer = PackObjectSizeIndexWriter
+				.createWriter(out, 0);
+		List<PackedObjectInfo> objs = new BlockList<>(9_000_000);
+		// The 24 bit range is full of commits and trees
+		PackedObjectInfo commit = objInfo(Constants.OBJ_COMMIT, 100);
+		for (int i = 0; i <= MAX_24BITS_UINT; i++) {
+			objs.add(commit);
+		}
+		objs.add(blobWithSize(100));
+		objs.add(blobWithSize(400));
+		objs.add(blobWithSize(200));
+
+		writer.write(objs);
+		byte[] expected = new byte[] { -1, 's', 'i', 'z', // header
+				0x01, // version
+				0x00, 0x00, 0x00, 0x00, // minimum object size
+				0x00, 0x00, 0x00, 0x03, // obj count
+				(byte) 0x20, // Signed 4 bytes
+				0x00, 0x00, 0x00, 0x03, // 3 positions
+				0x01, 0x00, 0x00, 0x00, // positions
+				0x01, 0x00, 0x00, 0x01, //
+				0x01, 0x00, 0x00, 0x02, //
+				0x00, // No more positions
+				0x00, 0x00, 0x00, 0x64, // size 100
+				0x00, 0x00, 0x01, (byte) 0x90, // size 400
+				0x00, 0x00, 0x00, (byte) 0xc8, // size 200
+				0x00, 0x00, 0x00, 0x00 // 64bit sizes counter
+		};
+		byte[] output = out.toByteArray();
+		assertArrayEquals(expected, output);
+	}
+
+	@Test
+	public void write_24b32bPositions_32bSizes() throws IOException {
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		PackObjectSizeIndexWriter writer = PackObjectSizeIndexWriter
+				.createWriter(out, 0);
+		List<PackedObjectInfo> objs = new BlockList<>(9_000_000);
+		// The 24 bit range is full of commits and trees
+		PackedObjectInfo commit = objInfo(Constants.OBJ_COMMIT, 100);
+		for (int i = 0; i < MAX_24BITS_UINT; i++) {
+			objs.add(commit);
+		}
+		objs.add(blobWithSize(100));
+		objs.add(blobWithSize(400));
+		objs.add(blobWithSize(200));
+
+		writer.write(objs);
+		byte[] expected = new byte[] { -1, 's', 'i', 'z', // header
+				0x01, // version
+				0x00, 0x00, 0x00, 0x00, // minimum object size
+				0x00, 0x00, 0x00, 0x03, // obj count
+				0x18, // 3 bytes
+				0x00, 0x00, 0x00, 0x01, // 1 position
+				(byte) 0xff, (byte) 0xff, (byte) 0xff,
+				(byte) 0x20, // 4 bytes (32 bits)
+				0x00, 0x00, 0x00, 0x02, // 2 positions
+				0x01, 0x00, 0x00, 0x00, // positions
+				0x01, 0x00, 0x00, 0x01, //
+				0x00, // No more positions
+				0x00, 0x00, 0x00, 0x64, // size 100
+				0x00, 0x00, 0x01, (byte) 0x90, // size 400
+				0x00, 0x00, 0x00, (byte) 0xc8, // size 200
+				0x00, 0x00, 0x00, 0x00 // 64bit sizes counter
+		};
+		byte[] output = out.toByteArray();
+		assertArrayEquals(expected, output);
+	}
+
+	@Test
+	public void write_64bitsSize() throws IOException {
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		PackObjectSizeIndexWriter writer = PackObjectSizeIndexWriter
+				.createWriter(out, 0);
+		List<PackedObjectInfo> objs = new ArrayList<>();
+		objs.add(blobWithSize(100));
+		objs.add(blobWithSize(3 * GB));
+		objs.add(blobWithSize(4 * GB));
+		objs.add(blobWithSize(400));
+
+		writer.write(objs);
+		byte[] expected = new byte[] { -1, 's', 'i', 'z', // header
+				0x01, // version
+				0x00, 0x00, 0x00, 0x00, // minimum object size
+				0x00, 0x00, 0x00, 0x04, // Object count
+				0x18, // Unsigned 3 byte positions
+				0x00, 0x00, 0x00, 0x04, // 4 positions
+				0x00, 0x00, 0x00, // positions
+				0x00, 0x00, 0x01, //
+				0x00, 0x00, 0x02, //
+				0x00, 0x00, 0x03, //
+				0x00, // No more positions
+				0x00, 0x00, 0x00, 0x64, // size 100
+				(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, // -1 (3GB)
+				(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xfe, // -2 (4GB)
+				0x00, 0x00, 0x01, (byte) 0x90, // size 400
+				0x00, 0x00, 0x00, (byte) 0x02, // 64bit sizes counter (2)
+				0x00, 0x00, 0x00, 0x00, // size 3Gb
+				(byte) 0xc0, 0x00, 0x00, 0x00, //
+				0x00, 0x00, 0x00, 0x01, // size 4GB
+				(byte) 0x00, 0x00, 0x00, 0x00, //
+				0x00, 0x00, 0x00, 0x00 // 128bit sizes counter
+		};
+		byte[] output = out.toByteArray();
+		assertArrayEquals(expected, output);
+	}
+
+	@Test
+	public void write_allObjectsTooSmall() throws IOException {
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		PackObjectSizeIndexWriter writer = PackObjectSizeIndexWriter
+				.createWriter(out, 1 << 10);
+		List<PackedObjectInfo> objs = new ArrayList<>();
+		// too small blobs
+		objs.add(blobWithSize(100));
+		objs.add(blobWithSize(200));
+		objs.add(blobWithSize(400));
+
+		writer.write(objs);
+		byte[] expected = new byte[] { -1, 's', 'i', 'z', // header
+				0x01, // version
+				0x00, 0x00, 0x04, 0x00, // minimum object size
+				0x00, 0x00, 0x00, 0x00, // Object count
+		};
+		byte[] output = out.toByteArray();
+		assertArrayEquals(expected, output);
+	}
+
+	@Test
+	public void write_onlyBlobsIndexed() throws IOException {
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		PackObjectSizeIndexWriter writer = PackObjectSizeIndexWriter
+				.createWriter(out, 0);
+		List<PackedObjectInfo> objs = new ArrayList<>();
+		objs.add(objInfo(Constants.OBJ_COMMIT, 1000));
+		objs.add(blobWithSize(100));
+		objs.add(objInfo(Constants.OBJ_TAG, 1000));
+		objs.add(blobWithSize(400));
+		objs.add(blobWithSize(200));
+
+		writer.write(objs);
+		byte[] expected = new byte[] { -1, 's', 'i', 'z', // header
+				0x01, // version
+				0x00, 0x00, 0x00, 0x00, // minimum object size
+				0x00, 0x00, 0x00, 0x03, // Object count
+				0x18, // Positions in 3 bytes
+				0x00, 0x00, 0x00, 0x03, // 3 entries
+				0x00, 0x00, 0x01, // positions
+				0x00, 0x00, 0x03, //
+				0x00, 0x00, 0x04, //
+				0x00, // No more positions
+				0x00, 0x00, 0x00, 0x64, // size 100
+				0x00, 0x00, 0x01, (byte) 0x90, // size 400
+				0x00, 0x00, 0x00, (byte) 0xc8, // size 200
+				0x00, 0x00, 0x00, 0x00 // 64bit sizes counter
+		};
+		byte[] output = out.toByteArray();
+		assertArrayEquals(expected, output);
+	}
+
+	@Test
+	public void write_noObjects() throws IOException {
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		PackObjectSizeIndexWriter writer = PackObjectSizeIndexWriter
+				.createWriter(out, 0);
+		List<PackedObjectInfo> objs = new ArrayList<>();
+
+		writer.write(objs);
+		byte[] expected = new byte[] { -1, 's', 'i', 'z', // header
+				0x01, // version
+				0x00, 0x00, 0x00, 0x00, // minimum object size
+				0x00, 0x00, 0x00, 0x00, // Object count
+		};
+		byte[] output = out.toByteArray();
+		assertArrayEquals(expected, output);
+	}
+
+	@Test
+	public void read_empty() throws IOException {
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		PackObjectSizeIndexWriter writer = PackObjectSizeIndexWriter
+				.createWriter(out, 0);
+		List<PackedObjectInfo> objs = new ArrayList<>();
+
+		writer.write(objs);
+		ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
+		PackObjectSizeIndex index = PackObjectSizeIndexLoader.load(in);
+		assertEquals(-1, index.getSize(0));
+		assertEquals(-1, index.getSize(1));
+		assertEquals(-1, index.getSize(1 << 30));
+		assertEquals(0, index.getThreshold());
+	}
+
+	@Test
+	public void read_only24bitsPositions() throws IOException {
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		PackObjectSizeIndexWriter writer = PackObjectSizeIndexWriter
+				.createWriter(out, 0);
+		List<PackedObjectInfo> objs = new ArrayList<>();
+		objs.add(blobWithSize(100));
+		objs.add(blobWithSize(200));
+		objs.add(blobWithSize(400));
+		objs.add(blobWithSize(1500));
+
+		writer.write(objs);
+		ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
+		PackObjectSizeIndex index = PackObjectSizeIndexLoader.load(in);
+		assertEquals(100, index.getSize(0));
+		assertEquals(200, index.getSize(1));
+		assertEquals(400, index.getSize(2));
+		assertEquals(1500, index.getSize(3));
+		assertEquals(0, index.getThreshold());
+	}
+
+	@Test
+	public void read_only32bitsPositions() throws IOException {
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		PackObjectSizeIndexWriter writer = PackObjectSizeIndexWriter
+				.createWriter(out, 2000);
+		List<PackedObjectInfo> objs = new ArrayList<>();
+		PackedObjectInfo smallObj = blobWithSize(100);
+		for (int i = 0; i <= MAX_24BITS_UINT; i++) {
+			objs.add(smallObj);
+		}
+		objs.add(blobWithSize(1000));
+		objs.add(blobWithSize(3000));
+		objs.add(blobWithSize(2500));
+		objs.add(blobWithSize(1000));
+
+		writer.write(objs);
+		ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
+		PackObjectSizeIndex index = PackObjectSizeIndexLoader.load(in);
+		assertEquals(-1, index.getSize(5));
+		assertEquals(-1, index.getSize(MAX_24BITS_UINT+1));
+		assertEquals(3000, index.getSize(MAX_24BITS_UINT+2));
+		assertEquals(2500, index.getSize(MAX_24BITS_UINT+3));
+		assertEquals(-1, index.getSize(MAX_24BITS_UINT+4)); // Not indexed
+		assertEquals(2000, index.getThreshold());
+	}
+
+	@Test
+	public void read_24and32BitsPositions() throws IOException {
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		PackObjectSizeIndexWriter writer = PackObjectSizeIndexWriter
+				.createWriter(out, 2000);
+		List<PackedObjectInfo> objs = new ArrayList<>();
+		PackedObjectInfo smallObj = blobWithSize(100);
+		for (int i = 0; i <= MAX_24BITS_UINT; i++) {
+			if (i == 500 || i == 1000 || i == 1500) {
+				objs.add(blobWithSize(2500));
+				continue;
+			}
+			objs.add(smallObj);
+		}
+		objs.add(blobWithSize(3000));
+		objs.add(blobWithSize(1000));
+		objs.add(blobWithSize(2500));
+		objs.add(blobWithSize(1000));
+
+		writer.write(objs);
+		ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
+		PackObjectSizeIndex index = PackObjectSizeIndexLoader.load(in);
+		// 24 bit positions
+		assertEquals(-1, index.getSize(5));
+		assertEquals(2500, index.getSize(500));
+		// 32 bit positions
+		assertEquals(3000, index.getSize(MAX_24BITS_UINT+1));
+		assertEquals(-1, index.getSize(MAX_24BITS_UINT+2));
+		assertEquals(2500, index.getSize(MAX_24BITS_UINT+3));
+		assertEquals(-1, index.getSize(MAX_24BITS_UINT+4)); // Not indexed
+		assertEquals(2000, index.getThreshold());
+	}
+
+	@Test
+	public void read_only64bits() throws IOException {
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		PackObjectSizeIndexWriter writer = PackObjectSizeIndexWriter
+				.createWriter(out, 0);
+		List<PackedObjectInfo> objs = new ArrayList<>();
+		objs.add(blobWithSize(3 * GB));
+		objs.add(blobWithSize(8 * GB));
+
+		writer.write(objs);
+		ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
+		PackObjectSizeIndex index = PackObjectSizeIndexLoader.load(in);
+		assertEquals(3 * GB, index.getSize(0));
+		assertEquals(8 * GB, index.getSize(1));
+		assertEquals(0, index.getThreshold());
+	}
+
+	@Test
+	public void read_withMinSize() throws IOException {
+		int minSize = 1000;
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		PackObjectSizeIndexWriter writer = PackObjectSizeIndexWriter
+				.createWriter(out, minSize);
+		List<PackedObjectInfo> objs = new ArrayList<>();
+		objs.add(blobWithSize(3 * GB));
+		objs.add(blobWithSize(1500));
+		objs.add(blobWithSize(500));
+		objs.add(blobWithSize(1000));
+		objs.add(blobWithSize(2000));
+
+		writer.write(objs);
+		ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
+		PackObjectSizeIndex index = PackObjectSizeIndexLoader.load(in);
+		assertEquals(3 * GB, index.getSize(0));
+		assertEquals(1500, index.getSize(1));
+		assertEquals(-1, index.getSize(2));
+		assertEquals(1000, index.getSize(3));
+		assertEquals(2000, index.getSize(4));
+		assertEquals(minSize, index.getThreshold());
+	}
+
+	private static PackedObjectInfo blobWithSize(long size) {
+		return objInfo(Constants.OBJ_BLOB, size);
+	}
+
+	private static PackedObjectInfo objInfo(int type, long size) {
+		PackedObjectInfo objectInfo = new PackedObjectInfo(OBJ_ID);
+		objectInfo.setType(type);
+		objectInfo.setFullSize(size);
+		return objectInfo;
+	}
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackWriterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackWriterTest.java
index 3fe8f52..2a403c7 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackWriterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackWriterTest.java
@@ -505,6 +505,43 @@
 	}
 
 	@Test
+	public void testWriteObjectSizeIndex_noDeltas() throws Exception {
+		config.setMinBytesForObjSizeIndex(0);
+		HashSet<ObjectId> interesting = new HashSet<>();
+		interesting.add(ObjectId
+				.fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"));
+
+		NullProgressMonitor m1 = NullProgressMonitor.INSTANCE;
+		writer = new PackWriter(config, db.newObjectReader());
+		writer.setUseBitmaps(false);
+		writer.setThin(false);
+		writer.setIgnoreMissingUninteresting(false);
+		writer.preparePack(m1, interesting, NONE);
+		writer.writePack(m1, m1, os);
+
+		PackIndex idx;
+		try (ByteArrayOutputStream is = new ByteArrayOutputStream()) {
+			writer.writeIndex(is);
+			idx = PackIndex.read(new ByteArrayInputStream(is.toByteArray()));
+		}
+
+		PackObjectSizeIndex objSizeIdx;
+		try (ByteArrayOutputStream objSizeStream = new ByteArrayOutputStream()) {
+			writer.writeObjectSizeIndex(objSizeStream);
+			objSizeIdx = PackObjectSizeIndexLoader.load(
+					new ByteArrayInputStream(objSizeStream.toByteArray()));
+		}
+		writer.close();
+
+		ObjectId knownBlob1 = ObjectId
+				.fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259");
+		ObjectId knownBlob2 = ObjectId
+				.fromString("6ff87c4664981e4397625791c8ea3bbb5f2279a3");
+		assertEquals(18009, objSizeIdx.getSize(idx.findPosition(knownBlob1)));
+		assertEquals(18787, objSizeIdx.getSize(idx.findPosition(knownBlob2)));
+	}
+
+	@Test
 	public void testExclude() throws Exception {
 		// TestRepository closes repo
 		FileRepository repo = createBareRepository();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/UInt24ArrayTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/UInt24ArrayTest.java
new file mode 100644
index 0000000..a96c217
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/UInt24ArrayTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2023, Google LLC
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.storage.file;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class UInt24ArrayTest {
+
+	private static final byte[] DATA = { 0x00, 0x00, 0x00, // 0
+			0x00, 0x00, 0x05, // 5
+			0x00, 0x00, 0x0a, // 10
+			0x00, 0x00, 0x0f, // 15
+			0x00, 0x00, 0x14, // 20
+			0x00, 0x00, 0x19, // 25
+			(byte) 0xff, 0x00, 0x00, // Uint with MSB=1
+			(byte) 0xff, (byte) 0xff, (byte) 0xff, // MAX
+	};
+
+	private static final UInt24Array asArray = new UInt24Array(DATA);
+
+	@Test
+	public void uInt24Array_size() {
+		assertEquals(8, asArray.size());
+	}
+
+	@Test
+	public void uInt24Array_get() {
+		assertEquals(0, asArray.get(0));
+		assertEquals(5, asArray.get(1));
+		assertEquals(10, asArray.get(2));
+		assertEquals(15, asArray.get(3));
+		assertEquals(20, asArray.get(4));
+		assertEquals(25, asArray.get(5));
+		assertEquals(0xff0000, asArray.get(6));
+		assertEquals(0xffffff, asArray.get(7));
+		assertThrows(IndexOutOfBoundsException.class, () -> asArray.get(9));
+	}
+
+	@Test
+	public void uInt24Array_getLastValue() {
+		assertEquals(0xffffff, asArray.getLastValue());
+	}
+
+	@Test
+	public void uInt24Array_find() {
+		assertEquals(0, asArray.binarySearch(0));
+		assertEquals(1, asArray.binarySearch(5));
+		assertEquals(2, asArray.binarySearch(10));
+		assertEquals(3, asArray.binarySearch(15));
+		assertEquals(4, asArray.binarySearch(20));
+		assertEquals(5, asArray.binarySearch(25));
+		assertEquals(6, asArray.binarySearch(0xff0000));
+		assertEquals(7, asArray.binarySearch(0xffffff));
+		assertThrows(IllegalArgumentException.class,
+				() -> asArray.binarySearch(Integer.MAX_VALUE));
+	}
+
+	@Test
+	public void uInt24Array_empty() {
+		Assert.assertTrue(UInt24Array.EMPTY.isEmpty());
+		assertEquals(0, UInt24Array.EMPTY.size());
+		assertEquals(-1, UInt24Array.EMPTY.binarySearch(1));
+		assertThrows(IndexOutOfBoundsException.class,
+				() -> UInt24Array.EMPTY.getLastValue());
+		assertThrows(IndexOutOfBoundsException.class,
+				() -> UInt24Array.EMPTY.get(0));
+	}
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/io/CancellableDigestOutputStreamTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/io/CancellableDigestOutputStreamTest.java
index 09a7c0b..58ed785 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/io/CancellableDigestOutputStreamTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/io/CancellableDigestOutputStreamTest.java
@@ -54,6 +54,11 @@
 		public boolean isCancelled() {
 			return cancelled;
 		}
+
+		@Override
+		public void showDuration(boolean enabled) {
+			// not implemented
+		}
 	}
 
 	@Test
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/memory/TernarySearchTreeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/memory/TernarySearchTreeTest.java
new file mode 100644
index 0000000..623c92f
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/memory/TernarySearchTreeTest.java
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2021, Matthias Sohn <matthias.sohn@sap.com> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.storage.memory;
+
+import static java.util.Map.entry;
+import static org.junit.Assert.assertArrayEquals;
+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 java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class TernarySearchTreeTest {
+
+	private TernarySearchTree<String> tree;
+
+	@Before
+	public void setup() {
+		tree = new TernarySearchTree<>();
+		tree.insert("foo", "1");
+		tree.insert("bar", "2");
+		tree.insert("foobar", "3");
+		tree.insert("foobaz", "4");
+		tree.insert("johndoe", "5");
+	}
+
+	@Test
+	public void testInsert() {
+		int initialSize = tree.size();
+		tree.insert("foobarbaz", "6");
+		assertEquals(initialSize + 1, tree.size());
+		assertEquals("6", tree.get("foobarbaz"));
+	}
+
+	@Test
+	public void testBatchInsert() {
+		Map<String, String> m = Map.ofEntries(
+				entry("refs/heads/master", "master"),
+				entry("refs/heads/stable-1.0", "stable-1.0"),
+				entry("refs/heads/stable-2.1", "stable-2.1"),
+				entry("refs/heads/stable-2.0", "stable-2.0"),
+				entry("refs/heads/stable-1.1", "stable-1.1"),
+				entry("refs/heads/stable-2.2", "stable-2.2"),
+				entry("aaa", "aaa"), entry("refs/tags/xyz", "xyz"),
+				entry("refs/tags/v1.1", "v1.1"),
+				entry("refs/tags/v2.2", "v2.2"),
+				entry("refs/tags/v1.0", "v1.0"),
+				entry("refs/tags/v2.1", "v2.1"),
+				entry("refs/tags/v2.0", "v2.0"));
+		tree.insert(m);
+		assertArrayEquals(
+				new String[] { "refs/heads/master", "refs/heads/stable-1.0",
+						"refs/heads/stable-1.1", "refs/heads/stable-2.0",
+						"refs/heads/stable-2.1", "refs/heads/stable-2.2" },
+				toArray(tree.getKeysWithPrefix("refs/heads/")));
+		assertArrayEquals(
+				new String[] { "refs/tags/v1.0", "refs/tags/v1.1",
+						"refs/tags/v2.0", "refs/tags/v2.1", "refs/tags/v2.2",
+						"refs/tags/xyz" },
+				toArray(tree.getKeysWithPrefix("refs/tags/")));
+		assertEquals("aaa", tree.get("aaa"));
+	}
+
+	@Test
+	public void testInsertWithNullKey() {
+		Exception exception = assertThrows(IllegalArgumentException.class,
+				() -> {
+					tree.insert(null, "42");
+				});
+		assertTrue(exception.getMessage()
+				.contains("TernarySearchTree key must not be null or empty"));
+	}
+
+	@Test
+	public void testOverwriteValue() {
+		int initialSize = tree.size();
+		tree.insert("foo", "overwritten");
+		assertEquals(initialSize, tree.size());
+		assertEquals("overwritten", tree.get("foo"));
+	}
+
+	@Test
+	public void testInsertNullValue() {
+		Exception exception = assertThrows(IllegalArgumentException.class,
+				() -> {
+					tree.insert("xxx", null);
+				});
+		assertTrue(exception.getMessage()
+				.contains("cannot insert null value into TernarySearchTree"));
+	}
+
+	@Test
+	public void testReloadAll() {
+		Map<String, String> map = Map.of("foo", "foo-value", "bar",
+				"bar-value");
+		tree.replace(map.entrySet());
+		assertArrayEquals(new String[] { "bar", "foo" },
+				toArray(tree.getKeys()));
+	}
+
+	@Test
+	public void testReload() {
+		int initialSize = tree.size();
+		Map<String, String> map = Map.of("foo", "foo-value", "bar",
+				"bar-value");
+		tree.reload(map.entrySet());
+		assertEquals("foo-value", tree.get("foo"));
+		assertEquals("bar-value", tree.get("bar"));
+		assertEquals("3", tree.get("foobar"));
+		assertEquals("4", tree.get("foobaz"));
+		assertEquals("5", tree.get("johndoe"));
+		assertEquals(initialSize, tree.size());
+	}
+
+	@Test
+	public void testContains() {
+		assertTrue(tree.contains("foo"));
+		assertTrue(tree.contains("foobaz"));
+		assertFalse(tree.contains("ship"));
+	}
+
+	@Test
+	public void testContainsWithNullKey() {
+		Exception exception = assertThrows(IllegalArgumentException.class,
+				() -> {
+					tree.contains(null);
+				});
+		assertTrue(exception.getMessage()
+				.contains("TernarySearchTree key must not be null or empty"));
+	}
+
+	@Test
+	public void testGet() {
+		assertEquals("1", tree.get("foo"));
+		assertEquals("5", tree.get("johndoe"));
+		assertNull(tree.get("ship"));
+	}
+
+	@Test
+	public void testGetWithNullKey() {
+		Exception exception = assertThrows(IllegalArgumentException.class,
+				() -> {
+					tree.get(null);
+				});
+		assertTrue(exception.getMessage()
+				.contains("TernarySearchTree key must not be null or empty"));
+	}
+
+	@Test
+	public void testDeleteExisting() {
+		int initialSize = tree.size();
+		tree.delete("foo");
+		assertNull(tree.get("foo"));
+		assertEquals(initialSize - 1, tree.size());
+		tree.delete("cake");
+		assertEquals(4, tree.size());
+	}
+
+	@Test
+	public void testDeleteNonExisting() {
+		int initialSize = tree.size();
+		tree.delete("non-existent-key");
+		assertEquals(initialSize, tree.size());
+	}
+
+	@Test
+	public void testDeleteWithNullKey() {
+		Exception exception = assertThrows(IllegalArgumentException.class,
+				() -> {
+					tree.delete((String) null);
+				});
+		assertTrue(exception.getMessage()
+				.contains("TernarySearchTree key must not be null or empty"));
+	}
+
+	@Test
+	public void testDeleteMultiple() {
+		int initialSize = tree.size();
+		List<String> keys = toList(tree.getKeys());
+		keys.remove("foobar");
+		keys.remove("johndoe");
+		tree.delete(Arrays.asList(new String[] { "foobar", "johndoe" }));
+		assertEquals(initialSize - 2, tree.size());
+		assertArrayEquals(keys.toArray(), toArray(tree.getKeys()));
+	}
+
+	@Test
+	public void testClear() {
+		assertEquals(5, tree.size());
+		tree.clear();
+		assertEquals(0, tree.size());
+		tree.getKeys().forEach(new Consumer<String>() {
+
+			@Override
+			public void accept(String t) {
+				throw new IllegalStateException("should find no key");
+			}
+		});
+	}
+
+	@Test
+	public void testKeyLongestPrefix() {
+		assertEquals("foobar", tree.keyLongestPrefixOf("foobari"));
+		assertEquals("foo", tree.keyLongestPrefixOf("foocake"));
+		assertEquals("", tree.keyLongestPrefixOf("faabar"));
+		assertEquals("johndoe", tree.keyLongestPrefixOf("johndoea"));
+		assertEquals("", tree.keyLongestPrefixOf("wxy"));
+		assertNull(tree.keyLongestPrefixOf(""));
+		assertNull(tree.keyLongestPrefixOf(null));
+	}
+
+	@Test
+	public void testGetKeys() {
+		assertArrayEquals(
+				new String[] { "bar", "foo", "foobar", "foobaz", "johndoe" },
+				toArray(tree.getKeys()));
+	}
+
+	@Test
+	public void testGetKeysWithPrefix() {
+		assertArrayEquals(new String[] { "foo", "foobar", "foobaz" },
+				toArray(tree.getKeysWithPrefix("foo")));
+		assertArrayEquals(new String[] { "johndoe" },
+				toArray(tree.getKeysWithPrefix("john")));
+		assertArrayEquals(new String[0],
+				toArray(tree.getKeysWithPrefix("cake")));
+		assertArrayEquals(
+				new String[] { "bar", "foo", "foobar", "foobaz", "johndoe" },
+				toArray(tree.getKeysWithPrefix("")));
+		assertArrayEquals(new String[0], toArray(tree.getKeysWithPrefix(null)));
+	}
+
+	@Test
+	public void testGetWithPrefixFoo() {
+		Map<String, String> result = tree.getWithPrefix("foo");
+		assertEquals(3, result.size());
+		assertEquals("1", result.get("foo"));
+		assertEquals("3", result.get("foobar"));
+		assertEquals("4", result.get("foobaz"));
+	}
+
+	@Test
+	public void testGetWithPrefixNotFound() {
+		Map<String, String> result = tree.getWithPrefix("cheese");
+		assertEquals(0, result.size());
+	}
+
+	@Test
+	public void testGetWithPrefixNull() {
+		Map<String, String> result = tree.getWithPrefix(null);
+		assertEquals(0, result.size());
+	}
+
+	@Test
+	public void testGetWithPrefixEmptyPrefix() {
+		Map<String, String> result = tree.getWithPrefix("");
+		assertEquals(5, result.size());
+		assertEquals("1", result.get("foo"));
+		assertEquals("2", result.get("bar"));
+		assertEquals("3", result.get("foobar"));
+		assertEquals("4", result.get("foobaz"));
+		assertEquals("5", result.get("johndoe"));
+	}
+
+	@Test
+	public void testGetValuesWithPrefixFoo() {
+		List<String> result = tree.getValuesWithPrefix("foo");
+		assertEquals(3, result.size());
+		assertArrayEquals(new String[] { "1", "3", "4" },
+				result.toArray());
+	}
+
+	@Test
+	public void testGetValuesWithPrefixNotFound() {
+		List<String> result = tree.getValuesWithPrefix("cheese");
+		assertEquals(0, result.size());
+	}
+
+	@Test
+	public void testGetValuesWithPrefixNull() {
+		List<String> result = tree.getValuesWithPrefix(null);
+		assertEquals(0, result.size());
+	}
+
+	@Test
+	public void testGetValuesWithPrefixEmptyPrefix() {
+		List<String> result = tree.getValuesWithPrefix("");
+		assertEquals(5, result.size());
+		assertArrayEquals(new String[] { "2", "1", "3", "4", "5" },
+				result.toArray());
+	}
+
+	@Test
+	public void testGetKeysMatching() {
+		assertArrayEquals(new String[] { "foobar" },
+				toArray(tree.getKeysMatching("fo?bar")));
+		assertArrayEquals(new String[] { "foobar", "foobaz" },
+				toArray(tree.getKeysMatching("fooba?")));
+		assertArrayEquals(new String[] { "foobar", "foobaz" },
+				toArray(tree.getKeysMatching("?o?ba?")));
+		assertArrayEquals(new String[0], toArray(tree.getKeysMatching("")));
+		assertArrayEquals(new String[0], toArray(tree.getKeysMatching(null)));
+	}
+
+	private static List<String> toList(Iterable<String> iter) {
+		return StreamSupport.stream(iter.spliterator(), false)
+				.collect(Collectors.toList());
+	}
+
+	private static String[] toArray(Iterable<String> iter) {
+		return toList(iter).toArray(new String[0]);
+	}
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/BranchConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/BranchConfigTest.java
index 1374ea2..2c9097f 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/BranchConfigTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/BranchConfigTest.java
@@ -14,9 +14,11 @@
 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 org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.BranchConfig.BranchRebaseMode;
 import org.junit.Test;
 
 public class BranchConfigTest {
@@ -114,17 +116,58 @@
 	}
 
 	@Test
+	public void testRebaseMode() {
+		Config c = parse("" //
+				+ "[branch \"undefined\"]\n"
+				+ "[branch \"false\"]\n"
+				+ "  rebase = false\n"
+				+ "[branch \"true\"]\n"
+				+ "  rebase = true\n"
+				+ "[branch \"interactive\"]\n"
+				+ "  rebase = interactive\n"
+				+ "[branch \"merges\"]\n"
+				+ "  rebase = merges\n"
+				+ "[branch \"preserve\"]\n"
+				+ "  rebase = preserve\n"
+				+ "[branch \"illegal\"]\n"
+				+ "  rebase = illegal\n");
+		assertEquals(BranchRebaseMode.NONE,
+				new BranchConfig(c, " undefined").getRebaseMode());
+		assertEquals(BranchRebaseMode.NONE,
+				new BranchConfig(c, "false").getRebaseMode());
+		assertEquals(BranchRebaseMode.REBASE,
+				new BranchConfig(c, "true").getRebaseMode());
+		assertEquals(BranchRebaseMode.INTERACTIVE,
+				new BranchConfig(c, "interactive").getRebaseMode());
+		assertEquals(BranchRebaseMode.MERGES,
+				new BranchConfig(c, "merges").getRebaseMode());
+		assertEquals(BranchRebaseMode.MERGES,
+				new BranchConfig(c, "preserve").getRebaseMode());
+		assertThrows(IllegalArgumentException.class,
+				() -> new BranchConfig(c, "illegal").getRebaseMode());
+	}
+
+	@Test
 	public void isRebase() {
 		Config c = parse("" //
 				+ "[branch \"undefined\"]\n"
 				+ "[branch \"false\"]\n"
 				+ "  rebase = false\n"
 				+ "[branch \"true\"]\n"
-				+ "  rebase = true\n");
+				+ "  rebase = true\n"
+				+ "[branch \"interactive\"]\n"
+				+ "  rebase = interactive\n"
+				+ "[branch \"merges\"]\n"
+				+ "  rebase = merges\n"
+				+ "[branch \"preserve\"]\n"
+				+ "  rebase = preserve\n");
 
 		assertFalse(new BranchConfig(c, "undefined").isRebase());
 		assertFalse(new BranchConfig(c, "false").isRebase());
 		assertTrue(new BranchConfig(c, "true").isRebase());
+		assertTrue(new BranchConfig(c, "interactive").isRebase());
+		assertTrue(new BranchConfig(c, "merges").isRebase());
+		assertTrue(new BranchConfig(c, "preserve").isRebase());
 	}
 
 	private static Config parse(String content) {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java
index a85a4f4..8f9d105 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java
@@ -1581,6 +1581,20 @@
 		config.get(CommitConfig.KEY).getCommitTemplateContent(repo);
 	}
 
+	@Test
+	public void testCoreCommitGraphConfig() {
+		Config config = new Config();
+		assertFalse(config.get(CoreConfig.KEY).enableCommitGraph());
+
+		config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
+				ConfigConstants.CONFIG_COMMIT_GRAPH, true);
+		assertTrue(config.get(CoreConfig.KEY).enableCommitGraph());
+
+		config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
+				ConfigConstants.CONFIG_COMMIT_GRAPH, false);
+		assertFalse(config.get(CoreConfig.KEY).enableCommitGraph());
+	}
+
 	private static void assertValueRoundTrip(String value)
 			throws ConfigInvalidException {
 		assertValueRoundTrip(value, value);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ThreadSafeProgressMonitorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ThreadSafeProgressMonitorTest.java
index 8f84155..e21ff58 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ThreadSafeProgressMonitorTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ThreadSafeProgressMonitorTest.java
@@ -167,5 +167,10 @@
 		public boolean isCancelled() {
 			return false;
 		}
+
+		@Override
+		public void showDuration(boolean enabled) {
+			// not implemented
+		}
 	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/PatchApplierTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/PatchApplierTest.java
index aeb3e61..893fd61 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/PatchApplierTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/PatchApplierTest.java
@@ -24,6 +24,7 @@
 import java.io.OutputStream;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
+import org.eclipse.jgit.annotations.Nullable;
 import org.eclipse.jgit.api.Git;
 import org.eclipse.jgit.api.errors.PatchApplyException;
 import org.eclipse.jgit.api.errors.PatchFormatException;
@@ -71,26 +72,21 @@
 			this.inCore = inCore;
 		}
 
+		void init(final String aName) throws Exception {
+			init(aName, true, true);
+		}
+
 		protected void init(String aName, boolean preExists, boolean postExists)
 				throws Exception {
-			/* Patch and pre/postimage are read from data org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ */
+			// Patch and pre/postimage are read from data
+			// org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/
 			this.name = aName;
 			if (postExists) {
-				postImage = IO
-						.readWholeStream(getTestResource(name + "_PostImage"), 0)
-						.array();
-				expectedText = new String(postImage, StandardCharsets.UTF_8);
+				expectedText = initPostImage(aName);
 			}
 
-			File f = new File(db.getWorkTree(), name);
 			if (preExists) {
-				preImage = IO
-						.readWholeStream(getTestResource(name + "_PreImage"), 0)
-						.array();
-				try (Git git = new Git(db)) {
-					Files.write(f.toPath(), preImage);
-					git.add().addFilepattern(name).call();
-				}
+				initPreImage(aName);
 			}
 			try (Git git = new Git(db)) {
 				RevCommit base = git.commit().setMessage("PreImage").call();
@@ -98,8 +94,22 @@
 			}
 		}
 
-		void init(final String aName) throws Exception {
-			init(aName, true, true);
+		protected void initPreImage(String aName) throws Exception {
+			File f = new File(db.getWorkTree(), aName);
+			preImage = IO
+					.readWholeStream(getTestResource(aName + "_PreImage"), 0)
+					.array();
+			try (Git git = new Git(db)) {
+				Files.write(f.toPath(), preImage);
+				git.add().addFilepattern(aName).call();
+			}
+		}
+
+		protected String initPostImage(String aName) throws Exception {
+			postImage = IO
+					.readWholeStream(getTestResource(aName + "_PostImage"), 0)
+					.array();
+			return new String(postImage, StandardCharsets.UTF_8);
 		}
 
 		protected Result applyPatch()
@@ -117,51 +127,64 @@
 			return PatchApplierTest.class.getClassLoader()
 					.getResourceAsStream("org/eclipse/jgit/diff/" + patchFile);
 		}
+
 		void verifyChange(Result result, String aName) throws Exception {
 			verifyChange(result, aName, true);
 		}
 
-		protected void verifyContent(Result result, String path, boolean exists) throws Exception {
+		protected void verifyContent(Result result, String path, boolean exists)
+				throws Exception {
+			verifyContent(result, path, exists ? expectedText : null);
+		}
+
+		protected void verifyContent(Result result, String path,
+				@Nullable String expectedContent) throws Exception {
 			if (inCore) {
 				byte[] output = readBlob(result.getTreeId(), path);
-				if (!exists)
+				if (expectedContent == null)
 					assertNull(output);
 				else {
 					assertNotNull(output);
-					assertEquals(new String(output, StandardCharsets.UTF_8), expectedText);
+					assertEquals(expectedContent,
+							new String(output, StandardCharsets.UTF_8));
 				}
 			} else {
 				File f = new File(db.getWorkTree(), path);
-				if (!exists)
+				if (expectedContent == null)
 					assertFalse(f.exists());
 				else
-					checkFile(f, expectedText);
+					checkFile(f, expectedContent);
 			}
 		}
 
-		void verifyChange(Result result, String aName, boolean exists) throws Exception {
+		void verifyChange(Result result, String aName, boolean exists)
+				throws Exception {
 			assertEquals(1, result.getPaths().size());
 			verifyContent(result, aName, exists);
 		}
 
-		protected byte[] readBlob(ObjectId treeish, String path) throws Exception {
+		protected byte[] readBlob(ObjectId treeish, String path)
+				throws Exception {
 			try (TestRepository<?> tr = new TestRepository<>(db);
 					RevWalk rw = tr.getRevWalk()) {
 				db.incrementOpen();
 				RevTree tree = rw.parseTree(treeish);
-				try (TreeWalk tw = TreeWalk.forPath(db,path,tree)){
+				try (TreeWalk tw = TreeWalk.forPath(db, path, tree)) {
 					if (tw == null) {
 						return null;
 					}
-					return tw.getObjectReader().open(tw.getObjectId(0), OBJ_BLOB).getBytes();
+					return tw.getObjectReader()
+							.open(tw.getObjectId(0), OBJ_BLOB).getBytes();
 				}
 			}
 		}
 
-		protected void checkBinary(Result result, int numberOfFiles) throws Exception {
+		protected void checkBinary(Result result, int numberOfFiles)
+				throws Exception {
 			assertEquals(numberOfFiles, result.getPaths().size());
 			if (inCore) {
-				assertArrayEquals(postImage, readBlob(result.getTreeId(), result.getPaths().get(0)));
+				assertArrayEquals(postImage,
+						readBlob(result.getTreeId(), result.getPaths().get(0)));
 			} else {
 				File f = new File(db.getWorkTree(), name);
 				assertArrayEquals(postImage, Files.readAllBytes(f.toPath()));
@@ -292,7 +315,7 @@
 			assertTrue(result.getPaths().contains("RenameNoHunks"));
 			assertTrue(result.getPaths().contains("nested/subdir/Renamed"));
 
-			verifyContent(result,"nested/subdir/Renamed", true);
+			verifyContent(result, "nested/subdir/Renamed", true);
 		}
 
 		@Test
@@ -304,7 +327,7 @@
 			assertTrue(result.getPaths().contains("RenameWithHunks"));
 			assertTrue(result.getPaths().contains("nested/subdir/Renamed"));
 
-			verifyContent(result,"nested/subdir/Renamed", true);
+			verifyContent(result, "nested/subdir/Renamed", true);
 		}
 
 		@Test
@@ -346,6 +369,17 @@
 			Result result = applyPatch();
 			verifyChange(result, "ShiftDown2");
 		}
+
+		@Test
+		public void testDoesNotAffectUnrelatedFiles() throws Exception {
+			initPreImage("Unaffected");
+			String expectedUnaffectedText = initPostImage("Unaffected");
+			init("X");
+
+			Result result = applyPatch();
+			verifyChange(result, "X");
+			verifyContent(result, "Unaffected", expectedUnaffectedText);
+		}
 	}
 
 	public static class InCore extends Base {
@@ -353,10 +387,44 @@
 		public InCore() {
 			super(true);
 		}
+
+		@Test
+		public void testNoNewlineAtEnd() throws Exception {
+			init("x_d");
+
+			Result result = applyPatch();
+			verifyChange(result, "x_d");
+		}
+
+		@Test
+		public void testNoNewlineAtEndInHunk() throws Exception {
+			init("x_e");
+
+			Result result = applyPatch();
+			verifyChange(result, "x_e");
+		}
+
+		@Test
+		public void testAddNewlineAtEnd() throws Exception {
+			init("x_add_nl");
+
+			Result result = applyPatch();
+			verifyChange(result, "x_add_nl");
+		}
+
+		@Test
+		public void testRemoveNewlineAtEnd() throws Exception {
+			init("x_last_rm_nl");
+
+			Result result = applyPatch();
+			verifyChange(result, "x_last_rm_nl");
+		}
 	}
 
 	public static class WithWorktree extends Base {
-		public WithWorktree() { super(false); }
+		public WithWorktree() {
+			super(false);
+		}
 
 		@Test
 		public void testModifyNL1() throws Exception {
@@ -369,8 +437,8 @@
 		@Test
 		public void testCrLf() throws Exception {
 			try {
-				db.getConfig().setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
-						ConfigConstants.CONFIG_KEY_AUTOCRLF, true);
+				db.getConfig().setBoolean(ConfigConstants.CONFIG_CORE_SECTION,
+						null, ConfigConstants.CONFIG_KEY_AUTOCRLF, true);
 				init("crlf", true, true);
 
 				Result result = applyPatch();
@@ -385,8 +453,8 @@
 		@Test
 		public void testCrLfOff() throws Exception {
 			try {
-				db.getConfig().setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
-						ConfigConstants.CONFIG_KEY_AUTOCRLF, false);
+				db.getConfig().setBoolean(ConfigConstants.CONFIG_CORE_SECTION,
+						null, ConfigConstants.CONFIG_KEY_AUTOCRLF, false);
 				init("crlf", true, true);
 
 				Result result = applyPatch();
@@ -401,8 +469,8 @@
 		@Test
 		public void testCrLfEmptyCommitted() throws Exception {
 			try {
-				db.getConfig().setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
-						ConfigConstants.CONFIG_KEY_AUTOCRLF, true);
+				db.getConfig().setBoolean(ConfigConstants.CONFIG_CORE_SECTION,
+						null, ConfigConstants.CONFIG_KEY_AUTOCRLF, true);
 				init("crlf3", true, true);
 
 				Result result = applyPatch();
@@ -417,8 +485,8 @@
 		@Test
 		public void testCrLfNewFile() throws Exception {
 			try {
-				db.getConfig().setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
-						ConfigConstants.CONFIG_KEY_AUTOCRLF, true);
+				db.getConfig().setBoolean(ConfigConstants.CONFIG_CORE_SECTION,
+						null, ConfigConstants.CONFIG_KEY_AUTOCRLF, true);
 				init("crlf4", false, true);
 
 				Result result = applyPatch();
@@ -433,8 +501,8 @@
 		@Test
 		public void testPatchWithCrLf() throws Exception {
 			try {
-				db.getConfig().setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
-						ConfigConstants.CONFIG_KEY_AUTOCRLF, false);
+				db.getConfig().setBoolean(ConfigConstants.CONFIG_CORE_SECTION,
+						null, ConfigConstants.CONFIG_KEY_AUTOCRLF, false);
 				init("crlf2", true, true);
 
 				Result result = applyPatch();
@@ -450,11 +518,11 @@
 		public void testPatchWithCrLf2() throws Exception {
 			String aName = "crlf2";
 			try (Git git = new Git(db)) {
-				db.getConfig().setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
-						ConfigConstants.CONFIG_KEY_AUTOCRLF, false);
+				db.getConfig().setBoolean(ConfigConstants.CONFIG_CORE_SECTION,
+						null, ConfigConstants.CONFIG_KEY_AUTOCRLF, false);
 				init(aName, true, true);
-				db.getConfig().setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
-						ConfigConstants.CONFIG_KEY_AUTOCRLF, true);
+				db.getConfig().setBoolean(ConfigConstants.CONFIG_CORE_SECTION,
+						null, ConfigConstants.CONFIG_KEY_AUTOCRLF, true);
 
 				Result result = applyPatch();
 
@@ -465,10 +533,234 @@
 			}
 		}
 
-		// Clean/smudge filter for testFiltering. The smudgetest test resources were
-		// created with C git using a clean filter sed -e "s/A/E/g" and the smudge
-		// filter sed -e "s/E/A/g". To keep the test independent of the presence of
-		// sed, implement this with a built-in filter.
+		@Test
+		public void testNoNewlineAtEndAutoCRLF_true() throws Exception {
+			try {
+				db.getConfig().setBoolean(ConfigConstants.CONFIG_CORE_SECTION,
+						null, ConfigConstants.CONFIG_KEY_AUTOCRLF, true);
+
+				init("x_d_crlf", true, true);
+
+				Result result = applyPatch();
+				verifyChange(result, "x_d_crlf");
+			} finally {
+				db.getConfig().unset(ConfigConstants.CONFIG_CORE_SECTION, null,
+						ConfigConstants.CONFIG_KEY_AUTOCRLF);
+			}
+		}
+
+		@Test
+		public void testNoNewlineAtEndAutoCRLF_false() throws Exception {
+			try {
+				db.getConfig().setBoolean(ConfigConstants.CONFIG_CORE_SECTION,
+						null, ConfigConstants.CONFIG_KEY_AUTOCRLF, false);
+
+				init("x_d", true, true);
+
+				Result result = applyPatch();
+				verifyChange(result, "x_d");
+			} finally {
+				db.getConfig().unset(ConfigConstants.CONFIG_CORE_SECTION, null,
+						ConfigConstants.CONFIG_KEY_AUTOCRLF);
+			}
+		}
+
+		@Test
+		public void testNoNewlineAtEndAutoCRLF_input() throws Exception {
+			try {
+				db.getConfig().setString(ConfigConstants.CONFIG_CORE_SECTION,
+						null, ConfigConstants.CONFIG_KEY_AUTOCRLF, "input");
+
+				init("x_d", true, true);
+
+				Result result = applyPatch();
+				verifyChange(result, "x_d");
+			} finally {
+				db.getConfig().unset(ConfigConstants.CONFIG_CORE_SECTION, null,
+						ConfigConstants.CONFIG_KEY_AUTOCRLF);
+			}
+		}
+
+		@Test
+		public void testNoNewlineAtEndInHunkAutoCRLF_true() throws Exception {
+			try {
+				db.getConfig().setBoolean(ConfigConstants.CONFIG_CORE_SECTION,
+						null, ConfigConstants.CONFIG_KEY_AUTOCRLF, true);
+
+				init("x_e_crlf", true, true);
+
+				Result result = applyPatch();
+				verifyChange(result, "x_e_crlf");
+			} finally {
+				db.getConfig().unset(ConfigConstants.CONFIG_CORE_SECTION, null,
+						ConfigConstants.CONFIG_KEY_AUTOCRLF);
+			}
+		}
+
+		@Test
+		public void testNoNewlineAtEndInHunkAutoCRLF_false() throws Exception {
+			try {
+				db.getConfig().setBoolean(ConfigConstants.CONFIG_CORE_SECTION,
+						null, ConfigConstants.CONFIG_KEY_AUTOCRLF, false);
+
+				init("x_e", true, true);
+
+				Result result = applyPatch();
+				verifyChange(result, "x_e");
+			} finally {
+				db.getConfig().unset(ConfigConstants.CONFIG_CORE_SECTION, null,
+						ConfigConstants.CONFIG_KEY_AUTOCRLF);
+			}
+		}
+
+		@Test
+		public void testNoNewlineAtEndInHunkAutoCRLF_input() throws Exception {
+			try {
+				db.getConfig().setString(ConfigConstants.CONFIG_CORE_SECTION,
+						null, ConfigConstants.CONFIG_KEY_AUTOCRLF, "input");
+
+				init("x_e", true, true);
+
+				Result result = applyPatch();
+				verifyChange(result, "x_e");
+			} finally {
+				db.getConfig().unset(ConfigConstants.CONFIG_CORE_SECTION, null,
+						ConfigConstants.CONFIG_KEY_AUTOCRLF);
+			}
+		}
+
+		@Test
+		public void testAddNewlineAtEndAutoCRLF_true() throws Exception {
+			try {
+				db.getConfig().setBoolean(ConfigConstants.CONFIG_CORE_SECTION,
+						null, ConfigConstants.CONFIG_KEY_AUTOCRLF, true);
+
+				init("x_add_nl_crlf", true, true);
+
+				Result result = applyPatch();
+				verifyChange(result, "x_add_nl_crlf");
+			} finally {
+				db.getConfig().unset(ConfigConstants.CONFIG_CORE_SECTION, null,
+						ConfigConstants.CONFIG_KEY_AUTOCRLF);
+			}
+		}
+
+		@Test
+		public void testAddNewlineAtEndAutoCRLF_false() throws Exception {
+			try {
+				db.getConfig().setBoolean(ConfigConstants.CONFIG_CORE_SECTION,
+						null, ConfigConstants.CONFIG_KEY_AUTOCRLF, false);
+
+				init("x_add_nl", true, true);
+
+				Result result = applyPatch();
+				verifyChange(result, "x_add_nl");
+			} finally {
+				db.getConfig().unset(ConfigConstants.CONFIG_CORE_SECTION, null,
+						ConfigConstants.CONFIG_KEY_AUTOCRLF);
+			}
+		}
+
+		@Test
+		public void testAddNewlineAtEndAutoCRLF_input() throws Exception {
+			try {
+				db.getConfig().setString(ConfigConstants.CONFIG_CORE_SECTION,
+						null, ConfigConstants.CONFIG_KEY_AUTOCRLF, "input");
+
+				init("x_add_nl", true, true);
+
+				Result result = applyPatch();
+				verifyChange(result, "x_add_nl");
+			} finally {
+				db.getConfig().unset(ConfigConstants.CONFIG_CORE_SECTION, null,
+						ConfigConstants.CONFIG_KEY_AUTOCRLF);
+			}
+		}
+
+		@Test
+		public void testRemoveNewlineAtEndAutoCRLF_true() throws Exception {
+			try {
+				db.getConfig().setBoolean(ConfigConstants.CONFIG_CORE_SECTION,
+						null, ConfigConstants.CONFIG_KEY_AUTOCRLF, true);
+
+				init("x_last_rm_nl_crlf", true, true);
+
+				Result result = applyPatch();
+				verifyChange(result, "x_last_rm_nl_crlf");
+			} finally {
+				db.getConfig().unset(ConfigConstants.CONFIG_CORE_SECTION, null,
+						ConfigConstants.CONFIG_KEY_AUTOCRLF);
+			}
+		}
+
+		@Test
+		public void testRemoveNewlineAtEndAutoCRLF_false() throws Exception {
+			try {
+				db.getConfig().setBoolean(ConfigConstants.CONFIG_CORE_SECTION,
+						null, ConfigConstants.CONFIG_KEY_AUTOCRLF, false);
+
+				init("x_last_rm_nl", true, true);
+
+				Result result = applyPatch();
+				verifyChange(result, "x_last_rm_nl");
+			} finally {
+				db.getConfig().unset(ConfigConstants.CONFIG_CORE_SECTION, null,
+						ConfigConstants.CONFIG_KEY_AUTOCRLF);
+			}
+		}
+
+		@Test
+		public void testRemoveNewlineAtEndAutoCRLF_input() throws Exception {
+			try {
+				db.getConfig().setString(ConfigConstants.CONFIG_CORE_SECTION,
+						null, ConfigConstants.CONFIG_KEY_AUTOCRLF, "input");
+
+				init("x_last_rm_nl", true, true);
+
+				Result result = applyPatch();
+				verifyChange(result, "x_last_rm_nl");
+			} finally {
+				db.getConfig().unset(ConfigConstants.CONFIG_CORE_SECTION, null,
+						ConfigConstants.CONFIG_KEY_AUTOCRLF);
+			}
+		}
+
+		@Test
+		public void testEditExample() throws Exception {
+			init("z_e", true, true);
+
+			Result result = applyPatch();
+			verifyChange(result, "z_e");
+		}
+
+		@Test
+		public void testEditNoNewline() throws Exception {
+			init("z_e_no_nl", true, true);
+
+			Result result = applyPatch();
+			verifyChange(result, "z_e_no_nl");
+		}
+
+		@Test
+		public void testEditAddNewline() throws Exception {
+			init("z_e_add_nl", true, true);
+
+			Result result = applyPatch();
+			verifyChange(result, "z_e_add_nl");
+		}
+
+		@Test
+		public void testEditRemoveNewline() throws Exception {
+			init("z_e_rm_nl", true, true);
+
+			Result result = applyPatch();
+			verifyChange(result, "z_e_rm_nl");
+		}
+
+		// Clean/smudge filter for testFiltering. The smudgetest test resources
+		// were created with C git using a clean filter sed -e "s/A/E/g" and the
+		// smudge filter sed -e "s/E/A/g". To keep the test independent of the
+		// presence of sed, implement this with a built-in filter.
 		private static class ReplaceFilter extends FilterCommand {
 
 			private final char toReplace;
@@ -501,8 +793,10 @@
 		@Test
 		public void testFiltering() throws Exception {
 			// Set up filter
-			FilterCommandFactory clean = (repo, in, out) -> new ReplaceFilter(in, out, 'A', 'E');
-			FilterCommandFactory smudge = (repo, in, out) -> new ReplaceFilter(in, out, 'E', 'A');
+			FilterCommandFactory clean =
+					(repo, in, out) -> new ReplaceFilter(in, out, 'A', 'E');
+			FilterCommandFactory smudge =
+					(repo, in, out) -> new ReplaceFilter(in, out, 'E', 'A');
 			FilterCommandRegistry.register("jgit://builtin/a2e/clean", clean);
 			FilterCommandRegistry.register("jgit://builtin/a2e/smudge", smudge);
 			Config config = db.getConfig();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkCommitGraphTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkCommitGraphTest.java
new file mode 100644
index 0000000..97d3f81
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkCommitGraphTest.java
@@ -0,0 +1,355 @@
+/*
+ * Copyright (C) 2023, Tencent.
+ *
+ * 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 org.eclipse.jgit.internal.storage.commitgraph.CommitGraph.EMPTY;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.internal.storage.file.GC;
+import org.eclipse.jgit.lib.ConfigConstants;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.revwalk.filter.MessageRevFilter;
+import org.eclipse.jgit.revwalk.filter.RevFilter;
+import org.eclipse.jgit.treewalk.filter.AndTreeFilter;
+import org.eclipse.jgit.treewalk.filter.PathFilter;
+import org.eclipse.jgit.treewalk.filter.TreeFilter;
+import org.junit.Test;
+
+public class RevWalkCommitGraphTest extends RevWalkTestCase {
+
+	private RevWalk rw;
+
+	@Override
+	public void setUp() throws Exception {
+		super.setUp();
+		rw = new RevWalk(db);
+	}
+
+	@Test
+	public void testParseHeaders() throws Exception {
+		RevCommit c1 = commitFile("file1", "1", "master");
+
+		RevCommit notParseInGraph = rw.lookupCommit(c1);
+		rw.parseHeaders(notParseInGraph);
+		assertFalse(notParseInGraph instanceof RevCommitCG);
+		assertNotNull(notParseInGraph.getRawBuffer());
+		assertEquals(Constants.COMMIT_GENERATION_UNKNOWN,
+				notParseInGraph.getGeneration());
+
+		enableAndWriteCommitGraph();
+
+		reinitializeRevWalk();
+		RevCommit parseInGraph = rw.lookupCommit(c1);
+		parseInGraph.parseHeaders(rw);
+
+		assertTrue(parseInGraph instanceof RevCommitCG);
+		assertNotNull(parseInGraph.getRawBuffer());
+		assertEquals(1, parseInGraph.getGeneration());
+		assertEquals(notParseInGraph.getId(), parseInGraph.getId());
+		assertEquals(notParseInGraph.getTree(), parseInGraph.getTree());
+		assertEquals(notParseInGraph.getCommitTime(), parseInGraph.getCommitTime());
+		assertArrayEquals(notParseInGraph.getParents(), parseInGraph.getParents());
+
+		reinitializeRevWalk();
+		rw.setRetainBody(false);
+		RevCommit noBody = rw.lookupCommit(c1);
+		noBody.parseHeaders(rw);
+
+		assertTrue(noBody instanceof RevCommitCG);
+		assertNull(noBody.getRawBuffer());
+		assertEquals(1, noBody.getGeneration());
+		assertEquals(notParseInGraph.getId(), noBody.getId());
+		assertEquals(notParseInGraph.getTree(), noBody.getTree());
+		assertEquals(notParseInGraph.getCommitTime(), noBody.getCommitTime());
+		assertArrayEquals(notParseInGraph.getParents(), noBody.getParents());
+	}
+
+	@Test
+	public void testParseCanonical() throws Exception {
+		RevCommit c1 = commitFile("file1", "1", "master");
+		enableAndWriteCommitGraph();
+
+		RevCommit notParseInGraph = rw.lookupCommit(c1);
+		rw.parseHeaders(notParseInGraph);
+
+		reinitializeRevWalk();
+		RevCommit parseInGraph = rw.lookupCommit(c1);
+		parseInGraph.parseCanonical(rw, rw.getCachedBytes(c1));
+
+		assertTrue(parseInGraph instanceof RevCommitCG);
+		assertNotNull(parseInGraph.getRawBuffer());
+		assertEquals(1, parseInGraph.getGeneration());
+		assertEquals(notParseInGraph.getId(), parseInGraph.getId());
+		assertEquals(notParseInGraph.getTree(), parseInGraph.getTree());
+		assertEquals(notParseInGraph.getCommitTime(),
+				parseInGraph.getCommitTime());
+		assertArrayEquals(notParseInGraph.getParents(),
+				parseInGraph.getParents());
+
+		reinitializeRevWalk();
+		rw.setRetainBody(false);
+		RevCommit noBody = rw.lookupCommit(c1);
+		noBody.parseCanonical(rw, rw.getCachedBytes(c1));
+
+		assertTrue(noBody instanceof RevCommitCG);
+		assertNull(noBody.getRawBuffer());
+		assertEquals(1, noBody.getGeneration());
+		assertEquals(notParseInGraph.getId(), noBody.getId());
+		assertEquals(notParseInGraph.getTree(), noBody.getTree());
+		assertEquals(notParseInGraph.getCommitTime(), noBody.getCommitTime());
+		assertArrayEquals(notParseInGraph.getParents(), noBody.getParents());
+	}
+
+	@Test
+	public void testInitializeShallowCommits() throws Exception {
+		RevCommit c1 = commit(commit());
+		branch(c1, "master");
+		enableAndWriteCommitGraph();
+		assertCommitCntInGraph(2);
+
+		db.getObjectDatabase().setShallowCommits(Collections.singleton(c1));
+		RevCommit parseInGraph = rw.lookupCommit(c1);
+		parseInGraph.parseHeaders(rw);
+
+		assertTrue(parseInGraph instanceof RevCommitCG);
+		assertNotNull(parseInGraph.getRawBuffer());
+		assertEquals(2, parseInGraph.getGeneration());
+		assertEquals(0, parseInGraph.getParentCount());
+	}
+
+	@Test
+	public void testTreeFilter() throws Exception {
+		RevCommit c1 = commitFile("file1", "1", "master");
+		RevCommit c2 = commitFile("file2", "2", "master");
+		RevCommit c3 = commitFile("file1", "3", "master");
+		RevCommit c4 = commitFile("file2", "4", "master");
+
+		enableAndWriteCommitGraph();
+		assertCommitCntInGraph(4);
+
+		rw.markStart(rw.lookupCommit(c4));
+		rw.setTreeFilter(AndTreeFilter.create(PathFilter.create("file1"),
+				TreeFilter.ANY_DIFF));
+		assertEquals(c3, rw.next());
+		assertEquals(c1, rw.next());
+		assertNull(rw.next());
+
+		reinitializeRevWalk();
+		rw.markStart(rw.lookupCommit(c4));
+		rw.setTreeFilter(AndTreeFilter.create(PathFilter.create("file2"),
+				TreeFilter.ANY_DIFF));
+		assertEquals(c4, rw.next());
+		assertEquals(c2, rw.next());
+		assertNull(rw.next());
+	}
+
+	@Test
+	public void testWalkWithCommitMessageFilter() throws Exception {
+		RevCommit a = commit();
+		RevCommit b = commitBuilder().parent(a)
+				.message("The quick brown fox jumps over the lazy dog!")
+				.create();
+		RevCommit c = commitBuilder().parent(b).message("commit-c").create();
+		branch(c, "master");
+
+		enableAndWriteCommitGraph();
+		assertCommitCntInGraph(3);
+
+		rw.setRevFilter(MessageRevFilter.create("quick brown fox jumps"));
+		rw.markStart(rw.lookupCommit(c));
+		assertEquals(b, rw.next());
+		assertNull(rw.next());
+	}
+
+	@Test
+	public void testCommitsWalk() throws Exception {
+		RevCommit c1 = commit();
+		branch(c1, "commits/1");
+		RevCommit c2 = commit(c1);
+		branch(c2, "commits/2");
+		RevCommit c3 = commit(c2);
+		branch(c3, "commits/3");
+
+		enableAndWriteCommitGraph();
+		assertCommitCntInGraph(3);
+		testRevWalkBehavior("commits/1", "commits/3");
+
+		// add more commits
+		RevCommit c4 = commit(c1);
+		RevCommit c5 = commit(c4);
+		RevCommit c6 = commit(c1);
+		RevCommit c7 = commit(c6);
+
+		RevCommit m1 = commit(c2, c4);
+		branch(m1, "merge/1");
+		RevCommit m2 = commit(c4, c6);
+		branch(m2, "merge/2");
+		RevCommit m3 = commit(c3, c5, c7);
+		branch(m3, "merge/3");
+
+		/*
+		 * <pre>
+		 * current graph structure:
+		 *
+		 *    __M3___
+		 *   /   |   \
+		 *  3 M1 5 M2 7
+		 *  |/  \|/  \|
+		 *  2    4    6
+		 *  |___/____/
+		 *  1
+		 * </pre>
+		 */
+		enableAndWriteCommitGraph();
+		reinitializeRevWalk();
+		assertCommitCntInGraph(10);
+		testRevWalkBehavior("merge/1", "merge/2");
+		testRevWalkBehavior("merge/1", "merge/3");
+		testRevWalkBehavior("merge/2", "merge/3");
+
+		// add one more commit
+		RevCommit c8 = commit(m3);
+		branch(c8, "commits/8");
+
+		/*
+		 * <pre>
+		 * current graph structure:
+		 *       8
+		 *       |
+		 *    __M3___
+		 *   /   |   \
+		 *  3 M1 5 M2 7
+		 *  |/  \|/  \|
+		 *  2    4    6
+		 *  |___/____/
+		 *  1
+		 * </pre>
+		 */
+		testRevWalkBehavior("commits/8", "merge/1");
+		testRevWalkBehavior("commits/8", "merge/2");
+
+		enableAndWriteCommitGraph();
+		reinitializeRevWalk();
+		assertCommitCntInGraph(11);
+		testRevWalkBehavior("commits/8", "merge/1");
+		testRevWalkBehavior("commits/8", "merge/2");
+	}
+
+	void testRevWalkBehavior(String branch, String compare) throws Exception {
+		assertCommits(
+				travel(TreeFilter.ALL, RevFilter.MERGE_BASE, RevSort.NONE, true,
+						branch, compare),
+				travel(TreeFilter.ALL, RevFilter.MERGE_BASE, RevSort.NONE,
+						false, branch, compare));
+
+		assertCommits(
+				travel(TreeFilter.ALL, RevFilter.ALL, RevSort.TOPO, true,
+						branch),
+				travel(TreeFilter.ALL, RevFilter.ALL, RevSort.TOPO, false,
+						branch));
+
+		assertCommits(
+				travel(TreeFilter.ALL, RevFilter.ALL, RevSort.TOPO, true,
+						compare),
+				travel(TreeFilter.ALL, RevFilter.ALL, RevSort.TOPO, false,
+						compare));
+
+		assertCommits(
+				travel(TreeFilter.ALL, RevFilter.ALL, RevSort.COMMIT_TIME_DESC,
+						true, branch),
+				travel(TreeFilter.ALL, RevFilter.ALL, RevSort.COMMIT_TIME_DESC,
+						false, branch));
+
+		assertCommits(
+				travel(TreeFilter.ALL, RevFilter.ALL, RevSort.COMMIT_TIME_DESC,
+						true, compare),
+				travel(TreeFilter.ALL, RevFilter.ALL, RevSort.COMMIT_TIME_DESC,
+						false, compare));
+	}
+
+	void assertCommitCntInGraph(int expect) {
+		assertEquals(expect, rw.commitGraph().getCommitCnt());
+	}
+
+	void assertCommits(List<RevCommit> expect, List<RevCommit> actual) {
+		assertEquals(expect.size(), actual.size());
+
+		for (int i = 0; i < expect.size(); i++) {
+			RevCommit c1 = expect.get(i);
+			RevCommit c2 = actual.get(i);
+
+			assertEquals(c1.getId(), c2.getId());
+			assertEquals(c1.getTree(), c2.getTree());
+			assertEquals(c1.getCommitTime(), c2.getCommitTime());
+			assertArrayEquals(c1.getParents(), c2.getParents());
+			assertArrayEquals(c1.getRawBuffer(), c2.getRawBuffer());
+		}
+	}
+
+	Ref branch(RevCommit commit, String name) throws Exception {
+		return Git.wrap(db).branchCreate().setName(name)
+				.setStartPoint(commit.name()).call();
+	}
+
+	List<RevCommit> travel(TreeFilter treeFilter, RevFilter revFilter,
+			RevSort revSort, boolean enableCommitGraph, String... starts)
+			throws Exception {
+		db.getConfig().setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
+				ConfigConstants.CONFIG_COMMIT_GRAPH, enableCommitGraph);
+
+		try (RevWalk walk = new RevWalk(db)) {
+			walk.setTreeFilter(treeFilter);
+			walk.setRevFilter(revFilter);
+			walk.sort(revSort);
+			walk.setRetainBody(false);
+			for (String start : starts) {
+				walk.markStart(walk.lookupCommit(db.resolve(start)));
+			}
+			List<RevCommit> commits = new ArrayList<>();
+
+			if (enableCommitGraph) {
+				assertTrue(walk.commitGraph().getCommitCnt() > 0);
+			} else {
+				assertEquals(EMPTY, walk.commitGraph());
+			}
+
+			for (RevCommit commit : walk) {
+				commits.add(commit);
+			}
+			return commits;
+		}
+	}
+
+	void enableAndWriteCommitGraph() throws Exception {
+		db.getConfig().setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
+				ConfigConstants.CONFIG_COMMIT_GRAPH, true);
+		db.getConfig().setBoolean(ConfigConstants.CONFIG_GC_SECTION, null,
+				ConfigConstants.CONFIG_KEY_WRITE_COMMIT_GRAPH, true);
+		GC gc = new GC(db);
+		gc.gc().get();
+	}
+
+	private void reinitializeRevWalk() {
+		rw.close();
+		rw = new RevWalk(db);
+	}
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java
index 7e6d6f2..61458dd 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java
@@ -2795,6 +2795,50 @@
 		assertEquals(1, stats.getNotAdvertisedWants());
 	}
 
+	@Test
+	public void testAllowAnySha1InWantConfig() {
+		server.getConfig().setBoolean("uploadpack", null, "allowanysha1inwant",
+				true);
+
+		try (UploadPack uploadPack = new UploadPack(server)) {
+			assertEquals(RequestPolicy.ANY, uploadPack.getRequestPolicy());
+		}
+	}
+
+	@Test
+	public void testAllowReachableSha1InWantConfig() {
+		server.getConfig().setBoolean("uploadpack", null,
+				"allowreachablesha1inwant", true);
+
+		try (UploadPack uploadPack = new UploadPack(server)) {
+			assertEquals(RequestPolicy.REACHABLE_COMMIT,
+					uploadPack.getRequestPolicy());
+		}
+	}
+
+	@Test
+	public void testAllowTipSha1InWantConfig() {
+		server.getConfig().setBoolean("uploadpack", null, "allowtipsha1inwant",
+				true);
+
+		try (UploadPack uploadPack = new UploadPack(server)) {
+			assertEquals(RequestPolicy.TIP, uploadPack.getRequestPolicy());
+		}
+	}
+
+	@Test
+	public void testAllowReachableTipSha1InWantConfig() {
+		server.getConfig().setBoolean("uploadpack", null,
+				"allowreachablesha1inwant", true);
+		server.getConfig().setBoolean("uploadpack", null, "allowtipsha1inwant",
+				true);
+
+		try (UploadPack uploadPack = new UploadPack(server)) {
+			assertEquals(RequestPolicy.REACHABLE_COMMIT_TIP,
+					uploadPack.getRequestPolicy());
+		}
+	}
+
 	private class RefCallsCountingRepository extends InMemoryRepository {
 		private final InMemoryRepository.MemRefDatabase refdb;
 		private int numRefCalls;
@@ -2835,7 +2879,9 @@
 		TestV2Hook hook = new TestV2Hook();
 		ByteArrayInputStream recvStream = uploadPackV2((UploadPack up) -> {
 			up.setProtocolV2Hook(hook);
-		}, "command=object-info\n", "size",
+		}, "command=object-info\n",
+				PacketLineIn.delimiter(),
+				"size",
 				"oid " + ObjectId.toString(blob1.getId()),
 				"oid " + ObjectId.toString(blob2.getId()), PacketLineIn.end());
 		PacketLineIn pckIn = new PacketLineIn(recvStream);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/TimeoutInputStreamTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/TimeoutInputStreamTest.java
index 6484169..76bda6a 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/TimeoutInputStreamTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/TimeoutInputStreamTest.java
@@ -91,7 +91,7 @@
 		final byte[] exp = new byte[] { 'a', 'b', 'c' };
 		final byte[] act = new byte[exp.length];
 		out.write(exp);
-		IO.readFully(is, act, 0, act.length);
+		IO.readFully(is, act);
 		assertArrayEquals(exp, act);
 	}
 
@@ -110,7 +110,7 @@
 	public void testTimeout_readBuffer_Timeout() throws IOException {
 		beginRead();
 		try {
-			IO.readFully(is, new byte[512], 0, 512);
+			IO.readFully(is, new byte[512]);
 			fail("incorrectly read bytes");
 		} catch (InterruptedIOException e) {
 			// expected
diff --git a/org.eclipse.jgit.ui/META-INF/MANIFEST.MF b/org.eclipse.jgit.ui/META-INF/MANIFEST.MF
index 0d3324d..2e61a7b 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.4.1.qualifier
+Bundle-Version: 6.5.1.qualifier
 Bundle-Vendor: %Bundle-Vendor
 Bundle-RequiredExecutionEnvironment: JavaSE-11
-Export-Package: org.eclipse.jgit.awtui;version="6.4.1"
-Import-Package: org.eclipse.jgit.errors;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.lib;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.nls;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.revplot;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.revwalk;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.transport;version="[6.4.1,6.5.0)",
- org.eclipse.jgit.util;version="[6.4.1,6.5.0)"
+Export-Package: org.eclipse.jgit.awtui;version="6.5.1"
+Import-Package: org.eclipse.jgit.errors;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.lib;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.nls;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.revplot;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.revwalk;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.transport;version="[6.5.1,6.6.0)",
+ org.eclipse.jgit.util;version="[6.5.1,6.6.0)"
diff --git a/org.eclipse.jgit.ui/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.ui/META-INF/SOURCE-MANIFEST.MF
index 4700584..edbf1f3 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.4.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.ui;version="6.4.1.qualifier";roots="."
+Bundle-Version: 6.5.1.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.ui;version="6.5.1.qualifier";roots="."
diff --git a/org.eclipse.jgit.ui/pom.xml b/org.eclipse.jgit.ui/pom.xml
index a46fdbc..625b757 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.4.1-SNAPSHOT</version>
+    <version>6.5.1-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.ui</artifactId>
diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF
index 7f18cfb..c5b5369 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.4.1.qualifier
+Bundle-Version: 6.5.1.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %Bundle-Vendor
 Eclipse-ExtensibleAPI: true
-Export-Package: org.eclipse.jgit.annotations;version="6.4.1",
- org.eclipse.jgit.api;version="6.4.1";
+Export-Package: org.eclipse.jgit.annotations;version="6.5.1",
+ org.eclipse.jgit.api;version="6.5.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.4.1";
+ org.eclipse.jgit.api.errors;version="6.5.1";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.errors",
- org.eclipse.jgit.attributes;version="6.4.1";
+ org.eclipse.jgit.attributes;version="6.5.1";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.treewalk",
- org.eclipse.jgit.blame;version="6.4.1";
+ org.eclipse.jgit.blame;version="6.5.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.4.1";
+ org.eclipse.jgit.diff;version="6.5.1";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.attributes,
    org.eclipse.jgit.revwalk,
@@ -42,51 +42,53 @@
    org.eclipse.jgit.treewalk.filter,
    org.eclipse.jgit.treewalk,
    org.eclipse.jgit.util",
- org.eclipse.jgit.dircache;version="6.4.1";
+ org.eclipse.jgit.dircache;version="6.5.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.4.1";
+ org.eclipse.jgit.errors;version="6.5.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.4.1";
+ org.eclipse.jgit.events;version="6.5.1";
   uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.fnmatch;version="6.4.1",
- org.eclipse.jgit.gitrepo;version="6.4.1";
+ org.eclipse.jgit.fnmatch;version="6.5.1",
+ org.eclipse.jgit.gitrepo;version="6.5.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.4.1";x-internal:=true,
- org.eclipse.jgit.hooks;version="6.4.1";uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.ignore;version="6.4.1",
- org.eclipse.jgit.ignore.internal;version="6.4.1";
+ org.eclipse.jgit.gitrepo.internal;version="6.5.1";x-internal:=true,
+ org.eclipse.jgit.hooks;version="6.5.1";uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.ignore;version="6.5.1",
+ org.eclipse.jgit.ignore.internal;version="6.5.1";
   x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal;version="6.4.1";
+ org.eclipse.jgit.internal;version="6.5.1";
   x-friends:="org.eclipse.jgit.test,
    org.eclipse.jgit.http.test",
- org.eclipse.jgit.internal.diff;version="6.4.1";
+ org.eclipse.jgit.internal.diff;version="6.5.1";
   x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal.diffmergetool;version="6.4.1";
+ org.eclipse.jgit.internal.diffmergetool;version="6.5.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.4.1";
+ org.eclipse.jgit.internal.fsck;version="6.5.1";
   x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal.revwalk;version="6.4.1";
+ org.eclipse.jgit.internal.revwalk;version="6.5.1";
   x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal.storage.dfs;version="6.4.1";
+ org.eclipse.jgit.internal.storage.commitgraph;version="6.5.1";
+  x-friends:="org.eclipse.jgit.test",
+ org.eclipse.jgit.internal.storage.dfs;version="6.5.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.4.1";
+ org.eclipse.jgit.internal.storage.file;version="6.5.1";
   x-friends:="org.eclipse.jgit.test,
    org.eclipse.jgit.junit,
    org.eclipse.jgit.junit.http,
@@ -95,32 +97,34 @@
    org.eclipse.jgit.pgm,
    org.eclipse.jgit.pgm.test,
    org.eclipse.jgit.ssh.apache",
- org.eclipse.jgit.internal.storage.io;version="6.4.1";
+ org.eclipse.jgit.internal.storage.io;version="6.5.1";
   x-friends:="org.eclipse.jgit.junit,
    org.eclipse.jgit.test,
    org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.storage.pack;version="6.4.1";
+ org.eclipse.jgit.internal.storage.memory;version="6.5.1";
+  x-friends:="org.eclipse.jgit.test",
+ org.eclipse.jgit.internal.storage.pack;version="6.5.1";
   x-friends:="org.eclipse.jgit.junit,
    org.eclipse.jgit.test,
    org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.storage.reftable;version="6.4.1";
+ org.eclipse.jgit.internal.storage.reftable;version="6.5.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.4.1";x-internal:=true,
- org.eclipse.jgit.internal.transport.connectivity;version="6.4.1";
+ org.eclipse.jgit.internal.submodule;version="6.5.1";x-internal:=true,
+ org.eclipse.jgit.internal.transport.connectivity;version="6.5.1";
   x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal.transport.http;version="6.4.1";
+ org.eclipse.jgit.internal.transport.http;version="6.5.1";
   x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal.transport.parser;version="6.4.1";
+ org.eclipse.jgit.internal.transport.parser;version="6.5.1";
   x-friends:="org.eclipse.jgit.http.server,
    org.eclipse.jgit.test",
- org.eclipse.jgit.internal.transport.ssh;version="6.4.1";
+ org.eclipse.jgit.internal.transport.ssh;version="6.5.1";
   x-friends:="org.eclipse.jgit.ssh.apache,
    org.eclipse.jgit.ssh.jsch,
    org.eclipse.jgit.test",
- org.eclipse.jgit.lib;version="6.4.1";
+ org.eclipse.jgit.lib;version="6.5.1";
   uses:="org.eclipse.jgit.transport,
    org.eclipse.jgit.util.sha1,
    org.eclipse.jgit.dircache,
@@ -134,12 +138,12 @@
    org.eclipse.jgit.util,
    org.eclipse.jgit.submodule,
    org.eclipse.jgit.util.time",
- org.eclipse.jgit.lib.internal;version="6.4.1";
+ org.eclipse.jgit.lib.internal;version="6.5.1";
   x-friends:="org.eclipse.jgit.test,
    org.eclipse.jgit.pgm,
    org.eclipse.egit.ui",
- org.eclipse.jgit.logging;version="6.4.1",
- org.eclipse.jgit.merge;version="6.4.1";
+ org.eclipse.jgit.logging;version="6.5.1",
+ org.eclipse.jgit.merge;version="6.5.1";
   uses:="org.eclipse.jgit.dircache,
    org.eclipse.jgit.lib,
    org.eclipse.jgit.revwalk,
@@ -148,40 +152,40 @@
    org.eclipse.jgit.util,
    org.eclipse.jgit.api,
    org.eclipse.jgit.attributes",
- org.eclipse.jgit.nls;version="6.4.1",
- org.eclipse.jgit.notes;version="6.4.1";
+ org.eclipse.jgit.nls;version="6.5.1",
+ org.eclipse.jgit.notes;version="6.5.1";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.revwalk,
    org.eclipse.jgit.treewalk,
    org.eclipse.jgit.merge",
- org.eclipse.jgit.patch;version="6.4.1";
+ org.eclipse.jgit.patch;version="6.5.1";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.diff",
- org.eclipse.jgit.revplot;version="6.4.1";
+ org.eclipse.jgit.revplot;version="6.5.1";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.revwalk",
- org.eclipse.jgit.revwalk;version="6.4.1";
+ org.eclipse.jgit.revwalk;version="6.5.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.4.1";
+ org.eclipse.jgit.revwalk.filter;version="6.5.1";
   uses:="org.eclipse.jgit.revwalk,
    org.eclipse.jgit.lib,
    org.eclipse.jgit.util",
- org.eclipse.jgit.storage.file;version="6.4.1";
+ org.eclipse.jgit.storage.file;version="6.5.1";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.util",
- org.eclipse.jgit.storage.pack;version="6.4.1";
+ org.eclipse.jgit.storage.pack;version="6.5.1";
   uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.submodule;version="6.4.1";
+ org.eclipse.jgit.submodule;version="6.5.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.4.1";
+ org.eclipse.jgit.transport;version="6.5.1";
   uses:="javax.crypto,
    org.eclipse.jgit.util.io,
    org.eclipse.jgit.lib,
@@ -194,21 +198,21 @@
    org.eclipse.jgit.transport.resolver,
    org.eclipse.jgit.storage.pack,
    org.eclipse.jgit.errors",
- org.eclipse.jgit.transport.http;version="6.4.1";
+ org.eclipse.jgit.transport.http;version="6.5.1";
   uses:="javax.net.ssl",
- org.eclipse.jgit.transport.resolver;version="6.4.1";
+ org.eclipse.jgit.transport.resolver;version="6.5.1";
   uses:="org.eclipse.jgit.transport,
    org.eclipse.jgit.lib",
- org.eclipse.jgit.treewalk;version="6.4.1";
+ org.eclipse.jgit.treewalk;version="6.5.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.4.1";
+ org.eclipse.jgit.treewalk.filter;version="6.5.1";
   uses:="org.eclipse.jgit.treewalk",
- org.eclipse.jgit.util;version="6.4.1";
+ org.eclipse.jgit.util;version="6.5.1";
   uses:="org.eclipse.jgit.transport,
    org.eclipse.jgit.hooks,
    org.eclipse.jgit.revwalk,
@@ -221,12 +225,12 @@
    org.eclipse.jgit.treewalk,
    javax.net.ssl,
    org.eclipse.jgit.util.time",
- org.eclipse.jgit.util.io;version="6.4.1";
+ org.eclipse.jgit.util.io;version="6.5.1";
   uses:="org.eclipse.jgit.attributes,
    org.eclipse.jgit.lib,
    org.eclipse.jgit.treewalk",
- org.eclipse.jgit.util.sha1;version="6.4.1",
- org.eclipse.jgit.util.time;version="6.4.1"
+ org.eclipse.jgit.util.sha1;version="6.5.1",
+ org.eclipse.jgit.util.time;version="6.5.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 28f3fa1..ab07518 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.4.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit;version="6.4.1.qualifier";roots="."
+Bundle-Version: 6.5.1.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit;version="6.5.1.qualifier";roots="."
diff --git a/org.eclipse.jgit/pom.xml b/org.eclipse.jgit/pom.xml
index 1a33e72..48f2327 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.4.1-SNAPSHOT</version>
+    <version>6.5.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 2460c15..faea9cb 100644
--- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
+++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
@@ -111,6 +111,7 @@
 cannotRead=Cannot read {0}
 cannotReadBackDelta=Cannot read delta type {0}
 cannotReadBlob=Cannot read blob {0}
+cannotReadByte=Cannot read byte from stream
 cannotReadCommit=Cannot read commit {0}
 cannotReadFile=Cannot read file {0}
 cannotReadHEAD=cannot read HEAD: {0} {1}
@@ -144,11 +145,17 @@
 commandClosedStderrButDidntExit=Command {0} closed stderr stream but didn''t exit within timeout {1} seconds
 commandRejectedByHook=Rejected by "{0}" hook.\n{1}
 commandWasCalledInTheWrongState=Command {0} was called in the wrong state
+commitGraphChunkNeeded=commit-graph 0x{0} chunk has not been loaded
+commitGraphChunkRepeated=commit-graph chunk id 0x{0} appears multiple times
+commitGraphChunkUnknown=unknown commit-graph chunk: 0x{0}
+commitGraphFileIsTooLargeForJgit=commit-graph file is too large for jgit
+commitGraphWritingCancelled=commit-graph writing was canceled
 commitMessageNotSpecified=commit message not specified
 commitOnRepoWithoutHEADCurrentlyNotSupported=Commit on repo without HEAD currently not supported
 commitAmendOnInitialNotPossible=Amending is not possible on initial commit.
 commitsHaveAlreadyBeenMarkedAsStart=Commits have already been marked as walk starts.
 compressingObjects=Compressing objects
+computingCommitGeneration=Computing commit-graph generation numbers
 configSubsectionContainsNewline=config subsection name contains newline
 configSubsectionContainsNullByte=config subsection name contains byte 0x00
 configValueContainsNullByte=config value contains byte 0x00
@@ -159,6 +166,7 @@
 contextMustBeNonNegative=context must be >= 0
 cookieFilePathRelative=git config http.cookieFile contains a relative path, should be absolute: {0}
 copyFileFailedNullFiles=Cannot copy file. Either origin or destination files are null
+corruptCommitGraph=commit-graph file {0} is corrupt
 corruptionDetectedReReadingAt=Corruption detected re-reading at {0}
 corruptObjectBadDate=bad date
 corruptObjectBadEmail=bad email
@@ -301,6 +309,7 @@
 exceptionOccurredDuringAddingOfOptionToALogCommand=Exception occurred during adding of {0} as option to a Log command
 exceptionOccurredDuringReadingOfGIT_DIR=Exception occurred during reading of $GIT_DIR/{0}. {1}
 exceptionWhileFindingUserHome=Problem determining the user home directory, trying Java user.home
+exceptionWhileLoadingCommitGraph=Exception caught while loading commit-graph file {0}, the commit-graph file might be corrupt.
 exceptionWhileReadingPack=Exception caught while accessing pack file {0}, the pack file might be corrupt. Caught {1} consecutive errors while trying to read this pack.
 expectedACKNAKFoundEOF=Expected ACK/NAK, found EOF
 expectedACKNAKGot=Expected ACK/NAK, got: {0}
@@ -330,6 +339,7 @@
 filterExecutionFailed=Execution of filter command ''{0}'' on file ''{1}'' failed
 filterExecutionFailedRc=Execution of filter command ''{0}'' on file ''{1}'' failed with return code ''{2}'', message on stderr: ''{3}''
 filterRequiresCapability=filter requires server to advertise that capability
+findingCommitsForCommitGraph=Finding commits for commit-graph
 findingGarbage=Finding garbage
 flagIsDisposed={0} is disposed.
 flagNotFromThis={0} not from this.
@@ -354,6 +364,8 @@
 illegalCombinationOfArguments=The combination of arguments {0} and {1} is not allowed
 illegalHookName=Illegal hook name {0}
 illegalPackingPhase=Illegal packing phase {0}
+illegalTernarySearchTreeKey=TernarySearchTree key must not be null or empty
+illegalTernarySearchTreeValue=cannot insert null value into TernarySearchTree
 incorrectHashFor=Incorrect hash for {0}; computed {1} as a {2} from {3} bytes.
 incorrectOBJECT_ID_LENGTH=Incorrect OBJECT_ID_LENGTH.
 indexFileCorruptedNegativeBucketCount=Invalid negative bucket count read from pack v2 index file: {0}
@@ -385,6 +397,7 @@
 invalidEncoding=Invalid encoding from git config i18n.commitEncoding: {0}
 invalidEncryption=Invalid encryption
 invalidExpandWildcard=ExpandFromSource on a refspec that can have mismatched wildcards does not make sense.
+invalidExtraEdgeListPosition=Invalid position in Extra Edge List chunk: {0}
 invalidFilter=Invalid filter: {0}
 invalidGitdirRef = Invalid .git reference in file ''{0}''
 invalidGitModules=Invalid .gitmodules file
@@ -515,6 +528,7 @@
 noSuchSubmodule=no such submodule {0}
 notABoolean=Not a boolean: {0}
 notABundle=not a bundle
+notACommitGraph=not a commit-graph
 notADIRCFile=Not a DIRC file.
 notAGitDirectory=not a git directory
 notAPACKFile=Not a PACK file.
@@ -528,6 +542,7 @@
 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.
+numberDoesntFit=Number doesn't fit in a single byte
 objectAtHasBadZlibStream=Object at {0} in {1} has bad zlib stream
 objectIsCorrupt=Object {0} is corrupt: {1}
 objectIsCorrupt3={0}: object {1}: {2}
@@ -764,6 +779,7 @@
 tSizeMustBeGreaterOrEqual1=tSize must be >= 1
 unableToCheckConnectivity=Unable to check connectivity.
 unableToCreateNewObject=Unable to create new object: {0}
+unableToReadFullInt=Unable to read a full int from the stream
 unableToReadPackfile=Unable to read packfile {0}
 unableToRemovePath=Unable to remove path ''{0}''
 unableToWrite=Unable to write {0}
@@ -789,6 +805,7 @@
 unknownObjectInIndex=unknown object {0} found in index but not in pack file
 unknownObjectType=Unknown object type {0}.
 unknownObjectType2=unknown
+unknownPositionEncoding=Unknown position encoding %s
 unknownRefStorageFormat=Unknown ref storage format "{0}"
 unknownRepositoryFormat=Unknown repository format
 unknownRepositoryFormat2=Unknown repository format "{0}"; expected "0".
@@ -798,6 +815,7 @@
 unmergedPath=Unmerged path: {0}
 unmergedPaths=Repository contains unmerged paths
 unpackException=Exception while parsing pack stream
+unreadableCommitGraph=Unreadable commit-graph: {0}
 unreadablePackIndex=Unreadable pack index: {0}
 unrecognizedPackExtension=Unrecognized pack extension: {0}
 unrecognizedRef=Unrecognized ref: {0}
@@ -805,6 +823,7 @@
 unsupportedAlternates=Alternates not supported
 unsupportedArchiveFormat=Unknown archive format ''{0}''
 unsupportedCommand0=unsupported command 0
+unsupportedCommitGraphVersion=Unsupported commit-graph version: {0}
 unsupportedEncryptionAlgorithm=Unsupported encryption algorithm: {0}
 unsupportedEncryptionVersion=Unsupported encryption version: {0}
 unsupportedGC=Unsupported garbage collector for repository type: {0}
@@ -814,6 +833,7 @@
 unsupportedPackVersion=Unsupported pack version {0}.
 unsupportedReftableVersion=Unsupported reftable version {0}.
 unsupportedRepositoryDescription=Repository description not supported
+unsupportedSizesObjSizeIndex=Unsupported sizes in object-size-index
 updateRequiresOldIdAndNewId=Update requires both old ID and new ID to be nonzero
 updatingHeadFailed=Updating HEAD failed
 updatingReferences=Updating references
@@ -843,6 +863,7 @@
 writeTimedOut=Write timed out after {0} ms
 writingNotPermitted=Writing not permitted
 writingNotSupported=Writing {0} not supported.
+writingOutCommitGraph=Writing out commit-graph in {0} passes
 writingObjects=Writing objects
 wrongDecompressedLength=wrong decompressed length
 wrongRepositoryState=Wrong Repository State: {0}
diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/storage/dfs/DfsText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/storage/dfs/DfsText.properties
index 2c4bd06..d9d43dc 100644
--- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/storage/dfs/DfsText.properties
+++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/storage/dfs/DfsText.properties
@@ -1,4 +1,5 @@
 cannotReadIndex=Cannot read index {0}
+cannotReadCommitGraph=Cannot read commit graph {0}
 shortReadOfBlock=Short read of block at {0} in pack {1}; expected {2} bytes, received only {3}
 shortReadOfIndex=Short read of index {0}
 willNotStoreEmptyPack=Cannot store empty pack
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java
index 281ecfd..83ae0fc 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java
@@ -137,8 +137,8 @@
 	 * <dt>BranchRebaseMode.REBASE</dt>
 	 * <dd>Equivalent to {@code --rebase} on the command line: use rebase
 	 * instead of merge after fetching.</dd>
-	 * <dt>BranchRebaseMode.PRESERVE</dt>
-	 * <dd>Equivalent to {@code --preserve-merges} on the command line: rebase
+	 * <dt>BranchRebaseMode.MERGES</dt>
+	 * <dd>Equivalent to {@code --rebase-merges} on the command line: rebase
 	 * preserving local merge commits.</dd>
 	 * <dt>BranchRebaseMode.INTERACTIVE</dt>
 	 * <dd>Equivalent to {@code --interactive} on the command line: use
@@ -362,7 +362,7 @@
 					.setStrategy(strategy)
 					.setContentMergeStrategy(contentStrategy)
 					.setPreserveMerges(
-							pullRebaseMode == BranchRebaseMode.PRESERVE)
+							pullRebaseMode == BranchRebaseMode.MERGES)
 					.call();
 			result = new PullResult(fetchRes, remote, rebaseRes);
 		} else {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
index 4e0d9d7..1e5523f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
@@ -151,7 +151,7 @@
 
 	/**
 	 * The folder containing the hashes of (potentially) rewritten commits when
-	 * --preserve-merges is used.
+	 * --rebase-merges is used.
 	 * <p>
 	 * Native git rebase --merge uses a <em>file</em> of that name to record
 	 * commits to copy notes at the end of the whole rebase.
@@ -160,7 +160,7 @@
 	private static final String REWRITTEN = "rewritten"; //$NON-NLS-1$
 
 	/**
-	 * File containing the current commit(s) to cherry pick when --preserve-merges
+	 * File containing the current commit(s) to cherry pick when --rebase-merges
 	 * is used.
 	 */
 	private static final String CURRENT_COMMIT = "current-commit"; //$NON-NLS-1$
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
index 7fc48d4..39cc749 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
@@ -139,6 +139,7 @@
 	/***/ public String cannotRead;
 	/***/ public String cannotReadBackDelta;
 	/***/ public String cannotReadBlob;
+	/***/ public String cannotReadByte;
 	/***/ public String cannotReadCommit;
 	/***/ public String cannotReadFile;
 	/***/ public String cannotReadHEAD;
@@ -172,11 +173,17 @@
 	/***/ public String commandClosedStderrButDidntExit;
 	/***/ public String commandRejectedByHook;
 	/***/ public String commandWasCalledInTheWrongState;
+	/***/ public String commitGraphChunkNeeded;
+	/***/ public String commitGraphChunkRepeated;
+	/***/ public String commitGraphChunkUnknown;
+	/***/ public String commitGraphFileIsTooLargeForJgit;
+	/***/ public String commitGraphWritingCancelled;
 	/***/ public String commitMessageNotSpecified;
 	/***/ public String commitOnRepoWithoutHEADCurrentlyNotSupported;
 	/***/ public String commitAmendOnInitialNotPossible;
 	/***/ public String commitsHaveAlreadyBeenMarkedAsStart;
 	/***/ public String compressingObjects;
+	/***/ public String computingCommitGeneration;
 	/***/ public String configSubsectionContainsNewline;
 	/***/ public String configSubsectionContainsNullByte;
 	/***/ public String configValueContainsNullByte;
@@ -187,6 +194,7 @@
 	/***/ public String contextMustBeNonNegative;
 	/***/ public String cookieFilePathRelative;
 	/***/ public String copyFileFailedNullFiles;
+	/***/ public String corruptCommitGraph;
 	/***/ public String corruptionDetectedReReadingAt;
 	/***/ public String corruptObjectBadDate;
 	/***/ public String corruptObjectBadEmail;
@@ -329,6 +337,7 @@
 	/***/ public String exceptionOccurredDuringAddingOfOptionToALogCommand;
 	/***/ public String exceptionOccurredDuringReadingOfGIT_DIR;
 	/***/ public String exceptionWhileFindingUserHome;
+	/***/ public String exceptionWhileLoadingCommitGraph;
 	/***/ public String exceptionWhileReadingPack;
 	/***/ public String expectedACKNAKFoundEOF;
 	/***/ public String expectedACKNAKGot;
@@ -358,6 +367,7 @@
 	/***/ public String filterExecutionFailed;
 	/***/ public String filterExecutionFailedRc;
 	/***/ public String filterRequiresCapability;
+	/***/ public String findingCommitsForCommitGraph;
 	/***/ public String findingGarbage;
 	/***/ public String flagIsDisposed;
 	/***/ public String flagNotFromThis;
@@ -382,6 +392,8 @@
 	/***/ public String illegalCombinationOfArguments;
 	/***/ public String illegalHookName;
 	/***/ public String illegalPackingPhase;
+	/***/ public String illegalTernarySearchTreeKey;
+	/***/ public String illegalTernarySearchTreeValue;
 	/***/ public String incorrectHashFor;
 	/***/ public String incorrectOBJECT_ID_LENGTH;
 	/***/ public String indexFileCorruptedNegativeBucketCount;
@@ -413,6 +425,7 @@
 	/***/ public String invalidEncoding;
 	/***/ public String invalidEncryption;
 	/***/ public String invalidExpandWildcard;
+	/***/ public String invalidExtraEdgeListPosition;
 	/***/ public String invalidFilter;
 	/***/ public String invalidGitdirRef;
 	/***/ public String invalidGitModules;
@@ -543,6 +556,7 @@
 	/***/ public String noSuchSubmodule;
 	/***/ public String notABoolean;
 	/***/ public String notABundle;
+	/***/ public String notACommitGraph;
 	/***/ public String notADIRCFile;
 	/***/ public String notAGitDirectory;
 	/***/ public String notAPACKFile;
@@ -556,6 +570,7 @@
 	/***/ public String notMergedExceptionMessage;
 	/***/ public String notShallowedUnshallow;
 	/***/ public String noXMLParserAvailable;
+	/***/ public String numberDoesntFit;
 	/***/ public String objectAtHasBadZlibStream;
 	/***/ public String objectIsCorrupt;
 	/***/ public String objectIsCorrupt3;
@@ -792,6 +807,7 @@
 	/***/ public String tSizeMustBeGreaterOrEqual1;
 	/***/ public String unableToCheckConnectivity;
 	/***/ public String unableToCreateNewObject;
+	/***/ public String unableToReadFullInt;
 	/***/ public String unableToReadPackfile;
 	/***/ public String unableToRemovePath;
 	/***/ public String unableToWrite;
@@ -817,6 +833,7 @@
 	/***/ public String unknownObjectInIndex;
 	/***/ public String unknownObjectType;
 	/***/ public String unknownObjectType2;
+	/***/ public String unknownPositionEncoding;
 	/***/ public String unknownRefStorageFormat;
 	/***/ public String unknownRepositoryFormat;
 	/***/ public String unknownRepositoryFormat2;
@@ -826,6 +843,7 @@
 	/***/ public String unmergedPath;
 	/***/ public String unmergedPaths;
 	/***/ public String unpackException;
+	/***/ public String unreadableCommitGraph;
 	/***/ public String unreadablePackIndex;
 	/***/ public String unrecognizedPackExtension;
 	/***/ public String unrecognizedRef;
@@ -833,6 +851,7 @@
 	/***/ public String unsupportedAlternates;
 	/***/ public String unsupportedArchiveFormat;
 	/***/ public String unsupportedCommand0;
+	/***/ public String unsupportedCommitGraphVersion;
 	/***/ public String unsupportedEncryptionAlgorithm;
 	/***/ public String unsupportedEncryptionVersion;
 	/***/ public String unsupportedGC;
@@ -842,6 +861,7 @@
 	/***/ public String unsupportedPackVersion;
 	/***/ public String unsupportedReftableVersion;
 	/***/ public String unsupportedRepositoryDescription;
+	/***/ public String unsupportedSizesObjSizeIndex;
 	/***/ public String updateRequiresOldIdAndNewId;
 	/***/ public String updatingHeadFailed;
 	/***/ public String updatingReferences;
@@ -871,6 +891,7 @@
 	/***/ public String writeTimedOut;
 	/***/ public String writingNotPermitted;
 	/***/ public String writingNotSupported;
+	/***/ public String writingOutCommitGraph;
 	/***/ public String writingObjects;
 	/***/ public String wrongDecompressedLength;
 	/***/ public String wrongRepositoryState;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/commitgraph/CommitGraph.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/commitgraph/CommitGraph.java
new file mode 100644
index 0000000..0796293
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/commitgraph/CommitGraph.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2022, Tencent.
+ *
+ * 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.commitgraph;
+
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.ObjectId;
+
+/**
+ * The CommitGraph is a supplemental data structure that accelerates commit
+ * graph walks.
+ * <p>
+ * If a user downgrades or disables the <code>core.commitGraph</code> config
+ * setting, then the existing object database is sufficient.
+ * </p>
+ * <p>
+ * It stores the commit graph structure along with some extra metadata to speed
+ * up graph walks. By listing commit OIDs in lexicographic order, we can
+ * identify an integer position for each commit and refer to the parents of a
+ * commit using those integer positions. We use binary search to find initial
+ * commits and then use the integer positions for fast lookups during the walk.
+ * </p>
+ */
+public interface CommitGraph {
+
+	/** Empty {@link CommitGraph} with no results. */
+	CommitGraph EMPTY = new CommitGraph() {
+		/** {@inheritDoc} */
+		@Override
+		public int findGraphPosition(AnyObjectId commit) {
+			return -1;
+		}
+
+		/** {@inheritDoc} */
+		@Override
+		public CommitData getCommitData(int graphPos) {
+			return null;
+		}
+
+		/** {@inheritDoc} */
+		@Override
+		public ObjectId getObjectId(int graphPos) {
+			return null;
+		}
+
+		/** {@inheritDoc} */
+		@Override
+		public long getCommitCnt() {
+			return 0;
+		}
+	};
+
+	/**
+	 * Find the position in the commit-graph of the commit.
+	 * <p>
+	 * The position can only be used within the CommitGraph Instance you got it
+	 * from. That's because the graph position of the same commit may be
+	 * different in CommitGraph obtained at different times (eg., regenerated
+	 * new commit-graph).
+	 *
+	 * @param commit
+	 *            the commit for which the commit-graph position will be found.
+	 * @return the commit-graph position or -1 if the object was not found.
+	 */
+	int findGraphPosition(AnyObjectId commit);
+
+	/**
+	 * Get the metadata of a commit。
+	 * <p>
+	 * This function runs in time O(1).
+	 * <p>
+	 * In the process of commit history traversal,
+	 * {@link CommitData#getParents()} makes us get the graphPos of the commit's
+	 * parents in advance, so that we can avoid O(logN) lookup and use O(1)
+	 * lookup instead.
+	 *
+	 * @param graphPos
+	 *            the position in the commit-graph of the object.
+	 * @return the metadata of a commit or null if it's not found.
+	 */
+	CommitData getCommitData(int graphPos);
+
+	/**
+	 * Get the object at the commit-graph position.
+	 *
+	 * @param graphPos
+	 *            the position in the commit-graph of the object.
+	 * @return the ObjectId or null if it's not found.
+	 */
+	ObjectId getObjectId(int graphPos);
+
+	/**
+	 * Obtain the total number of commits described by this commit-graph.
+	 *
+	 * @return number of commits in this commit-graph.
+	 */
+	long getCommitCnt();
+
+	/**
+	 * Metadata of a commit in commit data chunk.
+	 */
+	interface CommitData {
+
+		/**
+		 * Get a reference to this commit's tree.
+		 *
+		 * @return tree of this commit.
+		 */
+		ObjectId getTree();
+
+		/**
+		 * Obtain an array of all parents.
+		 * <p>
+		 * The method only provides the graph positions of parents in
+		 * commit-graph, call {@link CommitGraph#getObjectId(int)} to get the
+		 * real objectId.
+		 *
+		 * @return the array of parents.
+		 */
+		int[] getParents();
+
+		/**
+		 * Time from the "committer" line.
+		 *
+		 * @return the commit time in seconds since EPOCH.
+		 */
+		long getCommitTime();
+
+		/**
+		 * Get the generation number (the distance from the root) of the commit.
+		 *
+		 * @return the generation number or
+		 *         {@link org.eclipse.jgit.lib.Constants#COMMIT_GENERATION_NOT_COMPUTED}
+		 *         if the writer didn't calculate it.
+		 */
+		int getGeneration();
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphBuilder.java
new file mode 100644
index 0000000..a6af3bc
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphBuilder.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2022, Tencent.
+ *
+ * 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.commitgraph;
+
+import static org.eclipse.jgit.internal.storage.commitgraph.CommitGraphConstants.CHUNK_ID_COMMIT_DATA;
+import static org.eclipse.jgit.internal.storage.commitgraph.CommitGraphConstants.CHUNK_ID_EXTRA_EDGE_LIST;
+import static org.eclipse.jgit.internal.storage.commitgraph.CommitGraphConstants.CHUNK_ID_OID_FANOUT;
+import static org.eclipse.jgit.internal.storage.commitgraph.CommitGraphConstants.CHUNK_ID_OID_LOOKUP;
+import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH;
+
+import java.text.MessageFormat;
+
+import org.eclipse.jgit.internal.JGitText;
+
+/**
+ * Builder for {@link CommitGraph}.
+ */
+class CommitGraphBuilder {
+
+	private final int hashLength;
+
+	private byte[] oidFanout;
+
+	private byte[] oidLookup;
+
+	private byte[] commitData;
+
+	private byte[] extraList;
+
+	/** @return A builder of {@link CommitGraph}. */
+	static CommitGraphBuilder builder() {
+		return new CommitGraphBuilder(OBJECT_ID_LENGTH);
+	}
+
+	private CommitGraphBuilder(int hashLength) {
+		this.hashLength = hashLength;
+	}
+
+	CommitGraphBuilder addOidFanout(byte[] buffer)
+			throws CommitGraphFormatException {
+		assertChunkNotSeenYet(oidFanout, CHUNK_ID_OID_FANOUT);
+		oidFanout = buffer;
+		return this;
+	}
+
+	CommitGraphBuilder addOidLookUp(byte[] buffer)
+			throws CommitGraphFormatException {
+		assertChunkNotSeenYet(oidLookup, CHUNK_ID_OID_LOOKUP);
+		oidLookup = buffer;
+		return this;
+	}
+
+	CommitGraphBuilder addCommitData(byte[] buffer)
+			throws CommitGraphFormatException {
+		assertChunkNotSeenYet(commitData, CHUNK_ID_COMMIT_DATA);
+		commitData = buffer;
+		return this;
+	}
+
+	CommitGraphBuilder addExtraList(byte[] buffer)
+			throws CommitGraphFormatException {
+		assertChunkNotSeenYet(extraList, CHUNK_ID_EXTRA_EDGE_LIST);
+		extraList = buffer;
+		return this;
+	}
+
+	CommitGraph build() throws CommitGraphFormatException {
+		assertChunkNotNull(oidFanout, CHUNK_ID_OID_FANOUT);
+		assertChunkNotNull(oidLookup, CHUNK_ID_OID_LOOKUP);
+		assertChunkNotNull(commitData, CHUNK_ID_COMMIT_DATA);
+
+		GraphObjectIndex index = new GraphObjectIndex(hashLength, oidFanout,
+				oidLookup);
+		GraphCommitData commitDataChunk = new GraphCommitData(hashLength,
+				commitData, extraList);
+		return new CommitGraphV1(index, commitDataChunk);
+	}
+
+	private void assertChunkNotNull(Object object, int chunkId)
+			throws CommitGraphFormatException {
+		if (object == null) {
+			throw new CommitGraphFormatException(
+					MessageFormat.format(JGitText.get().commitGraphChunkNeeded,
+							Integer.toHexString(chunkId)));
+		}
+	}
+
+	private void assertChunkNotSeenYet(Object object, int chunkId)
+			throws CommitGraphFormatException {
+		if (object != null) {
+			throw new CommitGraphFormatException(MessageFormat.format(
+					JGitText.get().commitGraphChunkRepeated,
+					Integer.toHexString(chunkId)));
+		}
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphConstants.java
new file mode 100644
index 0000000..a074833
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphConstants.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2021, Tencent.
+ *
+ * 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.commitgraph;
+
+/**
+ * Constants relating to commit-graph.
+ */
+class CommitGraphConstants {
+
+	static final int COMMIT_GRAPH_MAGIC = 0x43475048; /* "CGPH" */
+
+	static final int CHUNK_ID_OID_FANOUT = 0x4f494446; /* "OIDF" */
+
+	static final int CHUNK_ID_OID_LOOKUP = 0x4f49444c; /* "OIDL" */
+
+	static final int CHUNK_ID_COMMIT_DATA = 0x43444154; /* "CDAT" */
+
+	static final int CHUNK_ID_EXTRA_EDGE_LIST = 0x45444745; /* "EDGE" */
+
+	/**
+	 * First 4 bytes describe the chunk id. Value 0 is a terminating label.
+	 * Other 8 bytes provide the byte-offset in current file for chunk to start.
+	 */
+	static final int CHUNK_LOOKUP_WIDTH = 12;
+
+	/**
+	 * First 8 bytes are for the positions of the first two parents of the ith
+	 * commit. The next 8 bytes store the generation number of the commit and
+	 * the commit time in seconds since EPOCH.
+	 */
+	static final int COMMIT_DATA_WIDTH = 16;
+
+	/** Mask to make the last edgeValue into position */
+	static final int GRAPH_EDGE_LAST_MASK = 0x7fffffff;
+
+	/** EdgeValue & GRAPH_LAST_EDGE != 0 means it is the last edgeValue */
+	static final int GRAPH_LAST_EDGE = 0x80000000;
+
+	/** EdgeValue == GRAPH_NO_PARENT means it has no parents */
+	static final int GRAPH_NO_PARENT = 0x70000000;
+
+	/**
+	 * EdgeValue & GRAPH_EXTRA_EDGES_NEEDED != 0 means its other parents are in
+	 * Chunk Extra Edge List
+	 */
+	static final int GRAPH_EXTRA_EDGES_NEEDED = 0x80000000;
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphFormatException.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphFormatException.java
new file mode 100644
index 0000000..352bf4b
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphFormatException.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2022, Tencent.
+ *
+ * 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.commitgraph;
+
+import java.io.IOException;
+
+/**
+ * Thrown when a commit-graph file's format is different from we expected
+ */
+public class CommitGraphFormatException extends IOException {
+
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Construct an exception.
+	 *
+	 * @param why
+	 *            description of the type of error.
+	 */
+	CommitGraphFormatException(String why) {
+		super(why);
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphLoader.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphLoader.java
new file mode 100644
index 0000000..571f5f4
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphLoader.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2022, Tencent.
+ *
+ * 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.commitgraph;
+
+import static org.eclipse.jgit.internal.storage.commitgraph.CommitGraphConstants.CHUNK_ID_COMMIT_DATA;
+import static org.eclipse.jgit.internal.storage.commitgraph.CommitGraphConstants.CHUNK_ID_EXTRA_EDGE_LIST;
+import static org.eclipse.jgit.internal.storage.commitgraph.CommitGraphConstants.CHUNK_ID_OID_FANOUT;
+import static org.eclipse.jgit.internal.storage.commitgraph.CommitGraphConstants.CHUNK_ID_OID_LOOKUP;
+import static org.eclipse.jgit.internal.storage.commitgraph.CommitGraphConstants.CHUNK_LOOKUP_WIDTH;
+import static org.eclipse.jgit.internal.storage.commitgraph.CommitGraphConstants.COMMIT_GRAPH_MAGIC;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.util.IO;
+import org.eclipse.jgit.util.NB;
+import org.eclipse.jgit.util.io.SilentFileInputStream;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The loader returns the representation of the commit-graph file content.
+ */
+public class CommitGraphLoader {
+
+	private final static Logger LOG = LoggerFactory
+			.getLogger(CommitGraphLoader.class);
+
+	/**
+	 * Open an existing commit-graph file for reading.
+	 * <p>
+	 * The format of the file will be automatically detected and a proper access
+	 * implementation for that format will be constructed and returned to the
+	 * caller. The file may or may not be held open by the returned instance.
+	 *
+	 * @param graphFile
+	 *            existing commit-graph to read.
+	 * @return a copy of the commit-graph file in memory
+	 * @throws FileNotFoundException
+	 *             the file does not exist.
+	 * @throws CommitGraphFormatException
+	 *             commit-graph file's format is different from we expected.
+	 * @throws java.io.IOException
+	 *             the file exists but could not be read due to security errors
+	 *             or unexpected data corruption.
+	 */
+	public static CommitGraph open(File graphFile) throws FileNotFoundException,
+			CommitGraphFormatException, IOException {
+		try (SilentFileInputStream fd = new SilentFileInputStream(graphFile)) {
+			try {
+				return read(fd);
+			} catch (CommitGraphFormatException fe) {
+				throw fe;
+			} catch (IOException ioe) {
+				throw new IOException(MessageFormat.format(
+						JGitText.get().unreadableCommitGraph,
+						graphFile.getAbsolutePath()), ioe);
+			}
+		}
+	}
+
+	/**
+	 * Read an existing commit-graph file from a buffered stream.
+	 * <p>
+	 * The format of the file will be automatically detected and a proper access
+	 * implementation for that format will be constructed and returned to the
+	 * caller. The file may or may not be held open by the returned instance.
+	 *
+	 * @param fd
+	 *            stream to read the commit-graph file from. The stream must be
+	 *            buffered as some small IOs are performed against the stream.
+	 *            The caller is responsible for closing the stream.
+	 *
+	 * @return a copy of the commit-graph file in memory
+	 * @throws CommitGraphFormatException
+	 *             the commit-graph file's format is different from we expected.
+	 * @throws java.io.IOException
+	 *             the stream cannot be read.
+	 */
+	public static CommitGraph read(InputStream fd)
+			throws CommitGraphFormatException, IOException {
+		byte[] hdr = new byte[8];
+		IO.readFully(fd, hdr, 0, hdr.length);
+
+		int magic = NB.decodeInt32(hdr, 0);
+		if (magic != COMMIT_GRAPH_MAGIC) {
+			throw new CommitGraphFormatException(
+					JGitText.get().notACommitGraph);
+		}
+
+		// Read the hash version (1 byte)
+		// 1 => SHA-1
+		// 2 => SHA-256 nonsupport now
+		int hashVersion = hdr[5];
+		if (hashVersion != 1) {
+			throw new CommitGraphFormatException(
+					JGitText.get().incorrectOBJECT_ID_LENGTH);
+		}
+
+		// Check commit-graph version
+		int v = hdr[4];
+		if (v != 1) {
+			throw new CommitGraphFormatException(MessageFormat.format(
+					JGitText.get().unsupportedCommitGraphVersion,
+					Integer.valueOf(v)));
+		}
+
+		// Read the number of "chunkOffsets" (1 byte)
+		int numberOfChunks = hdr[6];
+
+		// hdr[7] is the number of base commit-graphs, which is not supported in
+		// current version
+
+		byte[] lookupBuffer = new byte[CHUNK_LOOKUP_WIDTH
+				* (numberOfChunks + 1)];
+		IO.readFully(fd, lookupBuffer, 0, lookupBuffer.length);
+		List<ChunkSegment> chunks = new ArrayList<>(numberOfChunks + 1);
+		for (int i = 0; i <= numberOfChunks; i++) {
+			// chunks[numberOfChunks] is just a marker, in order to record the
+			// length of the last chunk.
+			int id = NB.decodeInt32(lookupBuffer, i * 12);
+			long offset = NB.decodeInt64(lookupBuffer, i * 12 + 4);
+			chunks.add(new ChunkSegment(id, offset));
+		}
+
+		CommitGraphBuilder builder = CommitGraphBuilder.builder();
+		for (int i = 0; i < numberOfChunks; i++) {
+			long chunkOffset = chunks.get(i).offset;
+			int chunkId = chunks.get(i).id;
+			long len = chunks.get(i + 1).offset - chunkOffset;
+
+			if (len > Integer.MAX_VALUE - 8) { // http://stackoverflow.com/a/8381338
+				throw new CommitGraphFormatException(
+						JGitText.get().commitGraphFileIsTooLargeForJgit);
+			}
+
+			byte buffer[] = new byte[(int) len];
+			IO.readFully(fd, buffer, 0, buffer.length);
+
+			switch (chunkId) {
+			case CHUNK_ID_OID_FANOUT:
+				builder.addOidFanout(buffer);
+				break;
+			case CHUNK_ID_OID_LOOKUP:
+				builder.addOidLookUp(buffer);
+				break;
+			case CHUNK_ID_COMMIT_DATA:
+				builder.addCommitData(buffer);
+				break;
+			case CHUNK_ID_EXTRA_EDGE_LIST:
+				builder.addExtraList(buffer);
+				break;
+			default:
+				LOG.warn(MessageFormat.format(
+						JGitText.get().commitGraphChunkUnknown,
+						Integer.toHexString(chunkId)));
+			}
+		}
+		return builder.build();
+	}
+
+	private static class ChunkSegment {
+		final int id;
+
+		final long offset;
+
+		private ChunkSegment(int id, long offset) {
+			this.id = id;
+			this.offset = offset;
+		}
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphV1.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphV1.java
new file mode 100644
index 0000000..da17219
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphV1.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2022, Tencent.
+ *
+ * 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.commitgraph;
+
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.ObjectId;
+
+/**
+ * Support for the commit-graph v1 format.
+ *
+ * @see CommitGraph
+ */
+class CommitGraphV1 implements CommitGraph {
+
+	private final GraphObjectIndex idx;
+
+	private final GraphCommitData commitData;
+
+	CommitGraphV1(GraphObjectIndex index, GraphCommitData commitData) {
+		this.idx = index;
+		this.commitData = commitData;
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public int findGraphPosition(AnyObjectId commit) {
+		return idx.findGraphPosition(commit);
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public CommitData getCommitData(int graphPos) {
+		if (graphPos < 0 || graphPos >= getCommitCnt()) {
+			return null;
+		}
+		return commitData.getCommitData(graphPos);
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public ObjectId getObjectId(int graphPos) {
+		return idx.getObjectId(graphPos);
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public long getCommitCnt() {
+		return idx.getCommitCnt();
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphWriter.java
new file mode 100644
index 0000000..a58a9eb
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphWriter.java
@@ -0,0 +1,338 @@
+/*
+ * Copyright (C) 2021, Tencent.
+ *
+ * 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.commitgraph;
+
+import static org.eclipse.jgit.internal.storage.commitgraph.CommitGraphConstants.CHUNK_ID_COMMIT_DATA;
+import static org.eclipse.jgit.internal.storage.commitgraph.CommitGraphConstants.CHUNK_ID_EXTRA_EDGE_LIST;
+import static org.eclipse.jgit.internal.storage.commitgraph.CommitGraphConstants.CHUNK_ID_OID_FANOUT;
+import static org.eclipse.jgit.internal.storage.commitgraph.CommitGraphConstants.CHUNK_ID_OID_LOOKUP;
+import static org.eclipse.jgit.internal.storage.commitgraph.CommitGraphConstants.CHUNK_LOOKUP_WIDTH;
+import static org.eclipse.jgit.internal.storage.commitgraph.CommitGraphConstants.COMMIT_DATA_WIDTH;
+import static org.eclipse.jgit.internal.storage.commitgraph.CommitGraphConstants.COMMIT_GRAPH_MAGIC;
+import static org.eclipse.jgit.internal.storage.commitgraph.CommitGraphConstants.GRAPH_EXTRA_EDGES_NEEDED;
+import static org.eclipse.jgit.internal.storage.commitgraph.CommitGraphConstants.GRAPH_LAST_EDGE;
+import static org.eclipse.jgit.internal.storage.commitgraph.CommitGraphConstants.GRAPH_NO_PARENT;
+import static org.eclipse.jgit.lib.Constants.COMMIT_GENERATION_NOT_COMPUTED;
+import static org.eclipse.jgit.lib.Constants.COMMIT_GENERATION_UNKNOWN;
+import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.io.OutputStream;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Stack;
+
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.io.CancellableDigestOutputStream;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ProgressMonitor;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.util.NB;
+
+/**
+ * Writes a commit-graph formatted file.
+ *
+ * @since 6.5
+ */
+public class CommitGraphWriter {
+
+	private static final int COMMIT_GRAPH_VERSION_GENERATED = 1;
+
+	private static final int OID_HASH_VERSION = 1;
+
+	private static final int GRAPH_FANOUT_SIZE = 4 * 256;
+
+	private static final int GENERATION_NUMBER_MAX = 0x3FFFFFFF;
+
+	private final int hashsz;
+
+	private final GraphCommits graphCommits;
+
+	/**
+	 * Create commit-graph writer for these commits.
+	 *
+	 * @param graphCommits
+	 *            the commits which will be writen to the commit-graph.
+	 */
+	public CommitGraphWriter(@NonNull GraphCommits graphCommits) {
+		this.graphCommits = graphCommits;
+		this.hashsz = OBJECT_ID_LENGTH;
+	}
+
+	/**
+	 * Write commit-graph to the supplied stream.
+	 *
+	 * @param monitor
+	 *            progress monitor to report the number of items written.
+	 * @param commitGraphStream
+	 *            output stream of commit-graph data. The stream should be
+	 *            buffered by the caller. The caller is responsible for closing
+	 *            the stream.
+	 * @throws IOException
+	 */
+	public void write(@NonNull ProgressMonitor monitor,
+			@NonNull OutputStream commitGraphStream) throws IOException {
+		if (graphCommits.size() == 0) {
+			return;
+		}
+
+		List<ChunkHeader> chunks = createChunks();
+		long writeCount = 256 + 2 * graphCommits.size()
+				+ graphCommits.getExtraEdgeCnt();
+		monitor.beginTask(
+				MessageFormat.format(JGitText.get().writingOutCommitGraph,
+						Integer.valueOf(chunks.size())),
+				(int) writeCount);
+
+		try (CancellableDigestOutputStream out = new CancellableDigestOutputStream(
+				monitor, commitGraphStream)) {
+			writeHeader(out, chunks.size());
+			writeChunkLookup(out, chunks);
+			writeChunks(monitor, out, chunks);
+			writeCheckSum(out);
+		} catch (InterruptedIOException e) {
+			throw new IOException(JGitText.get().commitGraphWritingCancelled,
+					e);
+		} finally {
+			monitor.endTask();
+		}
+	}
+
+	private List<ChunkHeader> createChunks() {
+		List<ChunkHeader> chunks = new ArrayList<>();
+		chunks.add(new ChunkHeader(CHUNK_ID_OID_FANOUT, GRAPH_FANOUT_SIZE));
+		chunks.add(new ChunkHeader(CHUNK_ID_OID_LOOKUP,
+				hashsz * graphCommits.size()));
+		chunks.add(new ChunkHeader(CHUNK_ID_COMMIT_DATA,
+				(hashsz + 16) * graphCommits.size()));
+		if (graphCommits.getExtraEdgeCnt() > 0) {
+			chunks.add(new ChunkHeader(CHUNK_ID_EXTRA_EDGE_LIST,
+					graphCommits.getExtraEdgeCnt() * 4));
+		}
+		return chunks;
+	}
+
+	private void writeHeader(CancellableDigestOutputStream out, int numChunks)
+			throws IOException {
+		byte[] headerBuffer = new byte[8];
+		NB.encodeInt32(headerBuffer, 0, COMMIT_GRAPH_MAGIC);
+		byte[] buff = { (byte) COMMIT_GRAPH_VERSION_GENERATED,
+				(byte) OID_HASH_VERSION, (byte) numChunks, (byte) 0 };
+		System.arraycopy(buff, 0, headerBuffer, 4, 4);
+		out.write(headerBuffer, 0, 8);
+		out.flush();
+	}
+
+	private void writeChunkLookup(CancellableDigestOutputStream out,
+			List<ChunkHeader> chunks) throws IOException {
+		int numChunks = chunks.size();
+		long chunkOffset = 8 + (numChunks + 1) * CHUNK_LOOKUP_WIDTH;
+		byte[] buffer = new byte[CHUNK_LOOKUP_WIDTH];
+		for (ChunkHeader chunk : chunks) {
+			NB.encodeInt32(buffer, 0, chunk.id);
+			NB.encodeInt64(buffer, 4, chunkOffset);
+			out.write(buffer);
+			chunkOffset += chunk.size;
+		}
+		NB.encodeInt32(buffer, 0, 0);
+		NB.encodeInt64(buffer, 4, chunkOffset);
+		out.write(buffer);
+	}
+
+	private void writeChunks(ProgressMonitor monitor,
+			CancellableDigestOutputStream out, List<ChunkHeader> chunks)
+			throws IOException {
+		for (ChunkHeader chunk : chunks) {
+			int chunkId = chunk.id;
+
+			switch (chunkId) {
+			case CHUNK_ID_OID_FANOUT:
+				writeFanoutTable(out);
+				break;
+			case CHUNK_ID_OID_LOOKUP:
+				writeOidLookUp(out);
+				break;
+			case CHUNK_ID_COMMIT_DATA:
+				writeCommitData(monitor, out);
+				break;
+			case CHUNK_ID_EXTRA_EDGE_LIST:
+				writeExtraEdges(out);
+				break;
+			}
+		}
+	}
+
+	private void writeCheckSum(CancellableDigestOutputStream out)
+			throws IOException {
+		out.write(out.getDigest());
+		out.flush();
+	}
+
+	private void writeFanoutTable(CancellableDigestOutputStream out)
+			throws IOException {
+		byte[] tmp = new byte[4];
+		int[] fanout = new int[256];
+		for (RevCommit c : graphCommits) {
+			fanout[c.getFirstByte() & 0xff]++;
+		}
+		for (int i = 1; i < fanout.length; i++) {
+			fanout[i] += fanout[i - 1];
+		}
+		for (int n : fanout) {
+			NB.encodeInt32(tmp, 0, n);
+			out.write(tmp, 0, 4);
+			out.getWriteMonitor().update(1);
+		}
+	}
+
+	private void writeOidLookUp(CancellableDigestOutputStream out)
+			throws IOException {
+		byte[] tmp = new byte[4 + hashsz];
+
+		for (RevCommit c : graphCommits) {
+			c.copyRawTo(tmp, 0);
+			out.write(tmp, 0, hashsz);
+			out.getWriteMonitor().update(1);
+		}
+	}
+
+	private void writeCommitData(ProgressMonitor monitor,
+			CancellableDigestOutputStream out) throws IOException {
+		int[] generations = computeGenerationNumbers(monitor);
+		int num = 0;
+		byte[] tmp = new byte[hashsz + COMMIT_DATA_WIDTH];
+		int i = 0;
+		for (RevCommit commit : graphCommits) {
+			int edgeValue;
+			int[] packedDate = new int[2];
+
+			ObjectId treeId = commit.getTree();
+			treeId.copyRawTo(tmp, 0);
+
+			RevCommit[] parents = commit.getParents();
+			if (parents.length == 0) {
+				edgeValue = GRAPH_NO_PARENT;
+			} else {
+				RevCommit parent = parents[0];
+				edgeValue = graphCommits.getOidPosition(parent);
+			}
+			NB.encodeInt32(tmp, hashsz, edgeValue);
+			if (parents.length == 1) {
+				edgeValue = GRAPH_NO_PARENT;
+			} else if (parents.length == 2) {
+				RevCommit parent = parents[1];
+				edgeValue = graphCommits.getOidPosition(parent);
+			} else if (parents.length > 2) {
+				edgeValue = GRAPH_EXTRA_EDGES_NEEDED | num;
+				num += parents.length - 1;
+			}
+
+			NB.encodeInt32(tmp, hashsz + 4, edgeValue);
+
+			packedDate[0] = 0; // commitTime is an int in JGit now
+			packedDate[0] |= generations[i] << 2;
+			packedDate[1] = commit.getCommitTime();
+			NB.encodeInt32(tmp, hashsz + 8, packedDate[0]);
+			NB.encodeInt32(tmp, hashsz + 12, packedDate[1]);
+
+			out.write(tmp);
+			out.getWriteMonitor().update(1);
+			i++;
+		}
+	}
+
+	private int[] computeGenerationNumbers(ProgressMonitor monitor)
+			throws MissingObjectException {
+		int[] generations = new int[graphCommits.size()];
+		monitor.beginTask(JGitText.get().computingCommitGeneration,
+				graphCommits.size());
+		for (RevCommit cmit : graphCommits) {
+			monitor.update(1);
+			int generation = generations[graphCommits.getOidPosition(cmit)];
+			if (generation != COMMIT_GENERATION_NOT_COMPUTED
+					&& generation != COMMIT_GENERATION_UNKNOWN) {
+				continue;
+			}
+
+			Stack<RevCommit> commitStack = new Stack<>();
+			commitStack.push(cmit);
+
+			while (!commitStack.empty()) {
+				int maxGeneration = 0;
+				boolean allParentComputed = true;
+				RevCommit current = commitStack.peek();
+				RevCommit parent;
+
+				for (int i = 0; i < current.getParentCount(); i++) {
+					parent = current.getParent(i);
+					generation = generations[graphCommits
+							.getOidPosition(parent)];
+					if (generation == COMMIT_GENERATION_NOT_COMPUTED
+							|| generation == COMMIT_GENERATION_UNKNOWN) {
+						allParentComputed = false;
+						commitStack.push(parent);
+						break;
+					} else if (generation > maxGeneration) {
+						maxGeneration = generation;
+					}
+				}
+
+				if (allParentComputed) {
+					RevCommit commit = commitStack.pop();
+					generation = maxGeneration + 1;
+					if (generation > GENERATION_NUMBER_MAX) {
+						generation = GENERATION_NUMBER_MAX;
+					}
+					generations[graphCommits
+							.getOidPosition(commit)] = generation;
+				}
+			}
+		}
+		monitor.endTask();
+		return generations;
+	}
+
+	private void writeExtraEdges(CancellableDigestOutputStream out)
+			throws IOException {
+		byte[] tmp = new byte[4];
+		for (RevCommit commit : graphCommits) {
+			RevCommit[] parents = commit.getParents();
+			if (parents.length > 2) {
+				int edgeValue;
+				for (int n = 1; n < parents.length; n++) {
+					RevCommit parent = parents[n];
+					edgeValue = graphCommits.getOidPosition(parent);
+					if (n == parents.length - 1) {
+						edgeValue |= GRAPH_LAST_EDGE;
+					}
+					NB.encodeInt32(tmp, 0, edgeValue);
+					out.write(tmp);
+					out.getWriteMonitor().update(1);
+				}
+			}
+		}
+	}
+
+	private static class ChunkHeader {
+		final int id;
+
+		final long size;
+
+		public ChunkHeader(int id, long size) {
+			this.id = id;
+			this.size = size;
+		}
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/commitgraph/GraphCommitData.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/commitgraph/GraphCommitData.java
new file mode 100644
index 0000000..6ae40ff
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/commitgraph/GraphCommitData.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2022, Tencent.
+ *
+ * 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.commitgraph;
+
+import static org.eclipse.jgit.internal.storage.commitgraph.CommitGraphConstants.COMMIT_DATA_WIDTH;
+import static org.eclipse.jgit.internal.storage.commitgraph.CommitGraphConstants.GRAPH_EDGE_LAST_MASK;
+import static org.eclipse.jgit.internal.storage.commitgraph.CommitGraphConstants.GRAPH_EXTRA_EDGES_NEEDED;
+import static org.eclipse.jgit.internal.storage.commitgraph.CommitGraphConstants.GRAPH_LAST_EDGE;
+import static org.eclipse.jgit.internal.storage.commitgraph.CommitGraphConstants.GRAPH_NO_PARENT;
+
+import java.text.MessageFormat;
+import java.util.Arrays;
+
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.commitgraph.CommitGraph.CommitData;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.util.NB;
+
+/**
+ * Represent the collection of {@link CommitData}.
+ */
+class GraphCommitData {
+
+	private static final int[] NO_PARENTS = {};
+
+	private final byte[] data;
+
+	private final byte[] extraList;
+
+	private final int hashLength;
+
+	private final int commitDataLength;
+
+	/**
+	 * Initialize the GraphCommitData.
+	 *
+	 * @param hashLength
+	 *            length of object hash.
+	 * @param commitData
+	 *            content of CommitData Chunk.
+	 * @param extraList
+	 *            content of Extra Edge List Chunk.
+	 */
+	GraphCommitData(int hashLength, @NonNull byte[] commitData,
+			byte[] extraList) {
+		this.data = commitData;
+		this.extraList = extraList;
+		this.hashLength = hashLength;
+		this.commitDataLength = hashLength + COMMIT_DATA_WIDTH;
+	}
+
+	/**
+	 * Get the metadata of a commit。
+	 *
+	 * @param graphPos
+	 *            the position in the commit-graph of the object.
+	 * @return the metadata of a commit or null if not found.
+	 */
+	CommitData getCommitData(int graphPos) {
+		int dataIdx = commitDataLength * graphPos;
+
+		// parse tree
+		ObjectId tree = ObjectId.fromRaw(data, dataIdx);
+
+		// parse date
+		long dateHigh = NB.decodeUInt32(data, dataIdx + hashLength + 8) & 0x3;
+		long dateLow = NB.decodeUInt32(data, dataIdx + hashLength + 12);
+		long commitTime = dateHigh << 32 | dateLow;
+
+		// parse generation
+		int generation = NB.decodeInt32(data, dataIdx + hashLength + 8) >> 2;
+
+		// parse first parent
+		int parent1 = NB.decodeInt32(data, dataIdx + hashLength);
+		if (parent1 == GRAPH_NO_PARENT) {
+			return new CommitDataImpl(tree, NO_PARENTS, commitTime, generation);
+		}
+
+		// parse second parent
+		int parent2 = NB.decodeInt32(data, dataIdx + hashLength + 4);
+		if (parent2 == GRAPH_NO_PARENT) {
+			return new CommitDataImpl(tree, new int[] { parent1 }, commitTime,
+					generation);
+		}
+
+		if ((parent2 & GRAPH_EXTRA_EDGES_NEEDED) == 0) {
+			return new CommitDataImpl(tree, new int[] { parent1, parent2 },
+					commitTime, generation);
+		}
+
+		// parse parents for octopus merge
+		return new CommitDataImpl(tree,
+				findParentsForOctopusMerge(parent1,
+						parent2 & GRAPH_EDGE_LAST_MASK),
+				commitTime, generation);
+	}
+
+	private int[] findParentsForOctopusMerge(int parent1, int extraEdgePos) {
+		int maxOffset = extraList.length - 4;
+		int offset = extraEdgePos * 4;
+		if (offset < 0 || offset > maxOffset) {
+			throw new IllegalArgumentException(MessageFormat.format(
+					JGitText.get().invalidExtraEdgeListPosition,
+					Integer.valueOf(extraEdgePos)));
+		}
+		int[] pList = new int[32];
+		pList[0] = parent1;
+		int count = 1;
+		int parentPosition;
+		for (; offset <= maxOffset; offset += 4) {
+			if (count >= pList.length) {
+				// expand the pList
+				pList = Arrays.copyOf(pList, pList.length + 32);
+			}
+			parentPosition = NB.decodeInt32(extraList, offset);
+			if ((parentPosition & GRAPH_LAST_EDGE) != 0) {
+				pList[count++] = parentPosition & GRAPH_EDGE_LAST_MASK;
+				break;
+			}
+			pList[count++] = parentPosition;
+		}
+		return Arrays.copyOf(pList, count);
+	}
+
+	private static class CommitDataImpl implements CommitData {
+
+		private final ObjectId tree;
+
+		private final int[] parents;
+
+		private final long commitTime;
+
+		private final int generation;
+
+		public CommitDataImpl(ObjectId tree, int[] parents, long commitTime,
+				int generation) {
+			this.tree = tree;
+			this.parents = parents;
+			this.commitTime = commitTime;
+			this.generation = generation;
+		}
+
+		@Override
+		public ObjectId getTree() {
+			return tree;
+		}
+
+		@Override
+		public int[] getParents() {
+			return parents;
+		}
+
+		@Override
+		public long getCommitTime() {
+			return commitTime;
+		}
+
+		@Override
+		public int getGeneration() {
+			return generation;
+		}
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/commitgraph/GraphCommits.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/commitgraph/GraphCommits.java
new file mode 100644
index 0000000..ccf6d0e
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/commitgraph/GraphCommits.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2021, Tencent.
+ *
+ * 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.commitgraph;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectIdOwnerMap;
+import org.eclipse.jgit.lib.ProgressMonitor;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.revwalk.RevSort;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.util.BlockList;
+
+/**
+ * The commits which are used by the commit-graph writer to:
+ * <ul>
+ * <li>List commits in SHA1 order.</li>
+ * <li>Get the position of a specific SHA1 in the list.</li>
+ * </ul>
+ *
+ * @since 6.5
+ */
+public class GraphCommits implements Iterable<RevCommit> {
+
+	/**
+	 * Prepare and create the commits for
+	 * {@link org.eclipse.jgit.internal.storage.commitgraph.CommitGraphWriter}
+	 * from the RevWalk.
+	 *
+	 * @param pm
+	 *            progress monitor.
+	 * @param wants
+	 *            the list of wanted objects, writer walks commits starting at
+	 *            these. Must not be {@code null}.
+	 * @param walk
+	 *            the RevWalk to use. Must not be {@code null}.
+	 * @return the commits' collection which are used by the commit-graph
+	 *         writer. Never null.
+	 * @throws IOException
+	 */
+	public static GraphCommits fromWalk(ProgressMonitor pm,
+			@NonNull Set<? extends ObjectId> wants, @NonNull RevWalk walk)
+			throws IOException {
+		walk.reset();
+		walk.sort(RevSort.NONE);
+		walk.setRetainBody(false);
+		for (ObjectId id : wants) {
+			RevObject o = walk.parseAny(id);
+			if (o instanceof RevCommit) {
+				walk.markStart((RevCommit) o);
+			}
+		}
+		List<RevCommit> commits = new BlockList<>();
+		RevCommit c;
+		pm.beginTask(JGitText.get().findingCommitsForCommitGraph,
+				ProgressMonitor.UNKNOWN);
+		while ((c = walk.next()) != null) {
+			pm.update(1);
+			commits.add(c);
+		}
+		pm.endTask();
+		return new GraphCommits(commits);
+	}
+
+	private final List<RevCommit> sortedCommits;
+
+	private final ObjectIdOwnerMap<CommitWithPosition> commitPosMap;
+
+	private final int extraEdgeCnt;
+
+	/**
+	 * Initialize the GraphCommits.
+	 *
+	 * @param commits
+	 *            list of commits with their headers already parsed.
+	 */
+	private GraphCommits(List<RevCommit> commits) {
+		Collections.sort(commits); // sorted by name
+		sortedCommits = commits;
+		commitPosMap = new ObjectIdOwnerMap<>();
+		int cnt = 0;
+		for (int i = 0; i < commits.size(); i++) {
+			RevCommit c = sortedCommits.get(i);
+			if (c.getParentCount() > 2) {
+				cnt += c.getParentCount() - 1;
+			}
+			commitPosMap.add(new CommitWithPosition(c, i));
+		}
+		this.extraEdgeCnt = cnt;
+	}
+
+	int getOidPosition(RevCommit c) throws MissingObjectException {
+		CommitWithPosition commitWithPosition = commitPosMap.get(c);
+		if (commitWithPosition == null) {
+			throw new MissingObjectException(c, Constants.OBJ_COMMIT);
+		}
+		return commitWithPosition.position;
+	}
+
+	int getExtraEdgeCnt() {
+		return extraEdgeCnt;
+	}
+
+	int size() {
+		return sortedCommits.size();
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public Iterator<RevCommit> iterator() {
+		return sortedCommits.iterator();
+	}
+
+	private static class CommitWithPosition extends ObjectIdOwnerMap.Entry {
+
+		final int position;
+
+		CommitWithPosition(AnyObjectId id, int position) {
+			super(id);
+			this.position = position;
+		}
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/commitgraph/GraphObjectIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/commitgraph/GraphObjectIndex.java
new file mode 100644
index 0000000..b0df467
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/commitgraph/GraphObjectIndex.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2022, Tencent.
+ *
+ * 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.commitgraph;
+
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.util.NB;
+
+/**
+ * The index which are used by the commit-graph to:
+ * <ul>
+ * <li>Get the object in commit-graph by using a specific position.</li>
+ * <li>Get the position of a specific object in commit-graph.</li>
+ * </ul>
+ */
+class GraphObjectIndex {
+
+	private static final int FANOUT = 256;
+
+	private final int hashLength;
+
+	private final int[] fanoutTable;
+
+	private final byte[] oidLookup;
+
+	private final long commitCnt;
+
+	/**
+	 * Initialize the GraphObjectIndex.
+	 *
+	 * @param hashLength
+	 *            length of object hash.
+	 * @param oidFanout
+	 *            content of OID Fanout Chunk.
+	 * @param oidLookup
+	 *            content of OID Lookup Chunk.
+	 * @throws CommitGraphFormatException
+	 *             commit-graph file's format is different from we expected.
+	 */
+	GraphObjectIndex(int hashLength, @NonNull byte[] oidFanout,
+			@NonNull byte[] oidLookup) throws CommitGraphFormatException {
+		this.hashLength = hashLength;
+		this.oidLookup = oidLookup;
+
+		int[] table = new int[FANOUT];
+		long uint32;
+		for (int k = 0; k < table.length; k++) {
+			uint32 = NB.decodeUInt32(oidFanout, k * 4);
+			if (table[k] > Integer.MAX_VALUE) {
+				throw new CommitGraphFormatException(
+						JGitText.get().commitGraphFileIsTooLargeForJgit);
+			}
+			table[k] = (int) uint32;
+		}
+		this.fanoutTable = table;
+		this.commitCnt = table[FANOUT - 1];
+	}
+
+	/**
+	 * Find the position in the commit-graph of the specified id.
+	 *
+	 * @param id
+	 *            the id for which the commit-graph position will be found.
+	 * @return the commit-graph position or -1 if the object was not found.
+	 */
+	int findGraphPosition(AnyObjectId id) {
+		int levelOne = id.getFirstByte();
+		int high = fanoutTable[levelOne];
+		int low = 0;
+		if (levelOne > 0) {
+			low = fanoutTable[levelOne - 1];
+		}
+		do {
+			int mid = (low + high) >>> 1;
+			int pos = objIdOffset(mid);
+			int cmp = id.compareTo(oidLookup, pos);
+			if (cmp < 0) {
+				high = mid;
+			} else if (cmp == 0) {
+				return mid;
+			} else {
+				low = mid + 1;
+			}
+		} while (low < high);
+		return -1;
+	}
+
+	/**
+	 * Get the object at the commit-graph position.
+	 *
+	 * @param graphPos
+	 *            the position in the commit-graph of the object.
+	 * @return the ObjectId or null if it's not found.
+	 */
+	ObjectId getObjectId(int graphPos) {
+		if (graphPos < 0 || graphPos >= commitCnt) {
+			return null;
+		}
+		return ObjectId.fromRaw(oidLookup, objIdOffset(graphPos));
+	}
+
+	long getCommitCnt() {
+		return commitCnt;
+	}
+
+	private int objIdOffset(int pos) {
+		return hashLength * pos;
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java
index 26d5b5b..66bcf73 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java
@@ -18,6 +18,7 @@
 import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.UNREACHABLE_GARBAGE;
 import static org.eclipse.jgit.internal.storage.dfs.DfsPackCompactor.configureReftable;
 import static org.eclipse.jgit.internal.storage.pack.PackExt.BITMAP_INDEX;
+import static org.eclipse.jgit.internal.storage.pack.PackExt.COMMIT_GRAPH;
 import static org.eclipse.jgit.internal.storage.pack.PackExt.INDEX;
 import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
 import static org.eclipse.jgit.internal.storage.pack.PackExt.REFTABLE;
@@ -34,8 +35,11 @@
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
 
 import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.commitgraph.CommitGraphWriter;
+import org.eclipse.jgit.internal.storage.commitgraph.GraphCommits;
 import org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource;
 import org.eclipse.jgit.internal.storage.file.PackIndex;
 import org.eclipse.jgit.internal.storage.file.PackReverseIndex;
@@ -75,6 +79,7 @@
 	private PackConfig packConfig;
 	private ReftableConfig reftableConfig;
 	private boolean convertToReftable = true;
+	private boolean writeCommitGraph;
 	private boolean includeDeletes;
 	private long reftableInitialMinUpdateIndex = 1;
 	private long reftableInitialMaxUpdateIndex = 1;
@@ -279,6 +284,20 @@
 	}
 
 	/**
+	 * Toggle commit graph generation.
+	 * <p>
+	 * False by default.
+	 *
+	 * @param enable
+	 *            Allow/Disallow commit graph generation.
+	 * @return {@code this}
+	 */
+	public DfsGarbageCollector setWriteCommitGraph(boolean enable) {
+		writeCommitGraph = enable;
+		return this;
+	}
+
+	/**
 	 * Create a single new pack file containing all of the live objects.
 	 * <p>
 	 * This method safely decides which packs can be expired after the new pack
@@ -642,6 +661,10 @@
 			writeReftable(pack);
 		}
 
+		if (source == GC) {
+			writeCommitGraph(pack, pm);
+		}
+
 		try (DfsOutputStream out = objdb.writeFile(pack, PACK)) {
 			pw.writePack(pm, pm, out);
 			pack.addFileExt(PACK);
@@ -724,4 +747,25 @@
 			pack.setReftableStats(writer.getStats());
 		}
 	}
+
+	private void writeCommitGraph(DfsPackDescription pack, ProgressMonitor pm)
+			throws IOException {
+		if (!writeCommitGraph || !objdb.getShallowCommits().isEmpty()) {
+			return;
+		}
+
+		Set<ObjectId> allTips = refsBefore.stream().map(Ref::getObjectId)
+				.collect(Collectors.toUnmodifiableSet());
+
+		try (DfsOutputStream out = objdb.writeFile(pack, COMMIT_GRAPH);
+				RevWalk pool = new RevWalk(ctx)) {
+			GraphCommits gcs = GraphCommits.fromWalk(pm, allTips, pool);
+			CountingOutputStream cnt = new CountingOutputStream(out);
+			CommitGraphWriter writer = new CommitGraphWriter(gcs);
+			writer.write(pm, cnt);
+			pack.addFileExt(COMMIT_GRAPH);
+			pack.setFileSize(COMMIT_GRAPH, cnt.getCount());
+			pack.setBlockSize(COMMIT_GRAPH, out.blockSize());
+		}
+	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java
index 15511fe..411777c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java
@@ -14,6 +14,7 @@
 
 import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.UNREACHABLE_GARBAGE;
 import static org.eclipse.jgit.internal.storage.pack.PackExt.BITMAP_INDEX;
+import static org.eclipse.jgit.internal.storage.pack.PackExt.COMMIT_GRAPH;
 import static org.eclipse.jgit.internal.storage.pack.PackExt.INDEX;
 import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
 import static org.eclipse.jgit.internal.storage.pack.PackExt.REVERSE_INDEX;
@@ -37,6 +38,8 @@
 import org.eclipse.jgit.errors.PackInvalidException;
 import org.eclipse.jgit.errors.StoredObjectRepresentationNotAvailableException;
 import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.commitgraph.CommitGraph;
+import org.eclipse.jgit.internal.storage.commitgraph.CommitGraphLoader;
 import org.eclipse.jgit.internal.storage.file.PackBitmapIndex;
 import org.eclipse.jgit.internal.storage.file.PackIndex;
 import org.eclipse.jgit.internal.storage.file.PackReverseIndex;
@@ -69,6 +72,9 @@
 	/** Index of compressed bitmap mapping entire object graph. */
 	private volatile PackBitmapIndex bitmapIndex;
 
+	/** Index of compressed commit graph mapping entire object graph. */
+	private volatile CommitGraph commitGraph;
+
 	/**
 	 * Objects we have tried to read, and discovered to be corrupt.
 	 * <p>
@@ -215,6 +221,43 @@
 		return bitmapIndex;
 	}
 
+	/**
+	 * Get the Commit Graph for this PackFile.
+	 *
+	 * @param ctx
+	 *            reader context to support reading from the backing store if
+	 *            the index is not already loaded in memory.
+	 * @return {@link org.eclipse.jgit.internal.storage.commitgraph.CommitGraph},
+	 *         null if pack doesn't have it.
+	 * @throws java.io.IOException
+	 *             the Commit Graph is not available, or is corrupt.
+	 */
+	public CommitGraph getCommitGraph(DfsReader ctx) throws IOException {
+		if (invalid || isGarbage() || !desc.hasFileExt(COMMIT_GRAPH)) {
+			return null;
+		}
+
+		if (commitGraph != null) {
+			return commitGraph;
+		}
+
+		DfsStreamKey commitGraphKey = desc.getStreamKey(COMMIT_GRAPH);
+		AtomicBoolean cacheHit = new AtomicBoolean(true);
+		DfsBlockCache.Ref<CommitGraph> cgref = cache
+				.getOrLoadRef(commitGraphKey, REF_POSITION, () -> {
+					cacheHit.set(false);
+					return loadCommitGraph(ctx, commitGraphKey);
+				});
+		if (cacheHit.get()) {
+			ctx.stats.commitGraphCacheHit++;
+		}
+		CommitGraph cg = cgref.get();
+		if (commitGraph == null && cg != null) {
+			commitGraph = cg;
+		}
+		return commitGraph;
+	}
+
 	PackReverseIndex getReverseIdx(DfsReader ctx) throws IOException {
 		if (reverseIndex != null) {
 			return reverseIndex;
@@ -1081,4 +1124,37 @@
 					desc.getFileName(BITMAP_INDEX)), e);
 		}
 	}
+
+	private DfsBlockCache.Ref<CommitGraph> loadCommitGraph(DfsReader ctx,
+			DfsStreamKey cgkey) throws IOException {
+		ctx.stats.readCommitGraph++;
+		long start = System.nanoTime();
+		try (ReadableChannel rc = ctx.db.openFile(desc, COMMIT_GRAPH)) {
+			long size;
+			CommitGraph cg;
+			try {
+				InputStream in = Channels.newInputStream(rc);
+				int wantSize = 8192;
+				int bs = rc.blockSize();
+				if (0 < bs && bs < wantSize) {
+					bs = (wantSize / bs) * bs;
+				} else if (bs <= 0) {
+					bs = wantSize;
+				}
+				in = new BufferedInputStream(in, bs);
+				cg = CommitGraphLoader.read(in);
+			} finally {
+				size = rc.position();
+				ctx.stats.readCommitGraphBytes += size;
+				ctx.stats.readCommitGraphMicros += elapsedMicros(start);
+			}
+			commitGraph = cg;
+			return new DfsBlockCache.Ref<>(cgkey, REF_POSITION, size, cg);
+		} catch (IOException e) {
+			throw new IOException(
+					MessageFormat.format(DfsText.get().cannotReadCommitGraph,
+							desc.getFileName(COMMIT_GRAPH)),
+					e);
+		}
+	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java
index d043b05..8d8a766 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java
@@ -23,6 +23,7 @@
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Optional;
 import java.util.Set;
 import java.util.zip.DataFormatException;
 import java.util.zip.Inflater;
@@ -31,6 +32,7 @@
 import org.eclipse.jgit.errors.MissingObjectException;
 import org.eclipse.jgit.errors.StoredObjectRepresentationNotAvailableException;
 import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.commitgraph.CommitGraph;
 import org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackList;
 import org.eclipse.jgit.internal.storage.file.BitmapIndexImpl;
 import org.eclipse.jgit.internal.storage.file.PackBitmapIndex;
@@ -123,6 +125,18 @@
 
 	/** {@inheritDoc} */
 	@Override
+	public Optional<CommitGraph> getCommitGraph() throws IOException {
+		for (DfsPackFile pack : db.getPacks()) {
+			CommitGraph cg = pack.getCommitGraph(this);
+			if (cg != null) {
+				return Optional.of(cg);
+			}
+		}
+		return Optional.empty();
+	}
+
+	/** {@inheritDoc} */
+	@Override
 	public Collection<CachedPack> getCachedPacksAndUpdate(
 		BitmapBuilder needBitmap) throws IOException {
 		for (DfsPackFile pack : db.getPacks()) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderIoStats.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderIoStats.java
index 5c47425..5ac7985 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderIoStats.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderIoStats.java
@@ -28,6 +28,9 @@
 		/** Total number of cache hits for bitmap indexes. */
 		long bitmapCacheHit;
 
+		/** Total number of cache hits for commit graphs. */
+		long commitGraphCacheHit;
+
 		/** Total number of complete pack indexes read into memory. */
 		long readIdx;
 
@@ -37,15 +40,24 @@
 		/** Total number of reverse indexes added into memory. */
 		long readReverseIdx;
 
+		/** Total number of complete commit graphs read into memory. */
+		long readCommitGraph;
+
 		/** Total number of bytes read from pack indexes. */
 		long readIdxBytes;
 
+		/** Total number of bytes read from commit graphs. */
+		long readCommitGraphBytes;
+
 		/** Total microseconds spent reading pack indexes. */
 		long readIdxMicros;
 
 		/** Total microseconds spent creating reverse indexes. */
 		long readReverseIdxMicros;
 
+		/** Total microseconds spent creating commit graphs. */
+		long readCommitGraphMicros;
+
 		/** Total number of bytes read from bitmap indexes. */
 		long readBitmapIdxBytes;
 
@@ -123,6 +135,15 @@
 	}
 
 	/**
+	 * Get total number of commit graph cache hits.
+	 *
+	 * @return total number of commit graph cache hits.
+	 */
+	public long getCommitGraphCacheHits() {
+		return stats.commitGraphCacheHit;
+	}
+
+	/**
 	 * Get total number of complete pack indexes read into memory.
 	 *
 	 * @return total number of complete pack indexes read into memory.
@@ -141,6 +162,15 @@
 	}
 
 	/**
+	 * Get total number of times the commit graph read into memory.
+	 *
+	 * @return total number of commit graph read into memory.
+	 */
+	public long getReadCommitGraphCount() {
+		return stats.readCommitGraph;
+	}
+
+	/**
 	 * Get total number of complete bitmap indexes read into memory.
 	 *
 	 * @return total number of complete bitmap indexes read into memory.
@@ -159,6 +189,15 @@
 	}
 
 	/**
+	 * Get total number of bytes read from commit graphs.
+	 *
+	 * @return total number of bytes read from commit graphs.
+	 */
+	public long getCommitGraphBytes() {
+		return stats.readCommitGraphBytes;
+	}
+
+	/**
 	 * Get total microseconds spent reading pack indexes.
 	 *
 	 * @return total microseconds spent reading pack indexes.
@@ -177,6 +216,15 @@
 	}
 
 	/**
+	 * Get total microseconds spent reading commit graphs.
+	 *
+	 * @return total microseconds spent reading commit graphs.
+	 */
+	public long getReadCommitGraphMicros() {
+		return stats.readCommitGraphMicros;
+	}
+
+	/**
 	 * Get total number of bytes read from bitmap indexes.
 	 *
 	 * @return total number of bytes read from bitmap indexes.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsText.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsText.java
index df565e5..f36ec06 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsText.java
@@ -28,6 +28,7 @@
 
 	// @formatter:off
 	/***/ public String cannotReadIndex;
+	/***/ public String cannotReadCommitGraph;
 	/***/ public String shortReadOfBlock;
 	/***/ public String shortReadOfIndex;
 	/***/ public String willNotStoreEmptyPack;
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 5a8207e..583b8b3 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
@@ -66,7 +66,16 @@
 	InMemoryRepository(Builder builder) {
 		super(builder);
 		objdb = new MemObjDatabase(this);
-		refdb = new MemRefDatabase();
+		refdb = createRefDatabase();
+	}
+
+	/**
+	 * Creates a new in-memory ref database.
+	 *
+	 * @return a new in-memory reference database.
+	 */
+	protected MemRefDatabase createRefDatabase() {
+		return new MemRefDatabase();
 	}
 
 	/** {@inheritDoc} */
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 9272bf3..2e19580 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
@@ -15,6 +15,7 @@
 import java.io.IOException;
 import java.util.Collection;
 import java.util.HashSet;
+import java.util.Optional;
 import java.util.Set;
 
 import org.eclipse.jgit.internal.storage.file.ObjectDirectory.AlternateHandle;
@@ -22,6 +23,7 @@
 import org.eclipse.jgit.internal.storage.pack.PackWriter;
 import org.eclipse.jgit.lib.AbbreviatedObjectId;
 import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.internal.storage.commitgraph.CommitGraph;
 import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectDatabase;
@@ -259,6 +261,12 @@
 		return wrapped.getPacks();
 	}
 
+	/** {@inheritDoc} */
+	@Override
+	public Optional<CommitGraph> getCommitGraph() {
+		return wrapped.getCommitGraph();
+	}
+
 	private static class UnpackedObjectId extends ObjectIdOwnerMap.Entry {
 		UnpackedObjectId(AnyObjectId id) {
 			super(id);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileCommitGraph.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileCommitGraph.java
new file mode 100644
index 0000000..44429a7
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileCommitGraph.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2022, Tencent.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.eclipse.jgit.internal.storage.file;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.commitgraph.CommitGraphFormatException;
+import org.eclipse.jgit.internal.storage.commitgraph.CommitGraphLoader;
+import org.eclipse.jgit.internal.storage.commitgraph.CommitGraph;
+import org.eclipse.jgit.lib.Constants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Traditional file system for commit-graph.
+ * <p>
+ * This is the commit-graph file representation for a Git object database. Each
+ * call to {@link FileCommitGraph#get()} will recheck for newer versions.
+ */
+public class FileCommitGraph {
+	private final static Logger LOG = LoggerFactory
+			.getLogger(FileCommitGraph.class);
+
+	private final AtomicReference<GraphSnapshot> baseGraph;
+
+	/**
+	 * Initialize a reference to an on-disk commit-graph.
+	 *
+	 * @param objectsDir
+	 *            the location of the <code>objects</code> directory.
+	 */
+	FileCommitGraph(File objectsDir) {
+		this.baseGraph = new AtomicReference<>(new GraphSnapshot(
+				new File(objectsDir, Constants.INFO_COMMIT_GRAPH)));
+	}
+
+	/**
+	 * The method will first scan whether the ".git/objects/info/commit-graph"
+	 * has been modified, if so, it will re-parse the file, otherwise it will
+	 * return the same result as the last time.
+	 *
+	 * @return commit-graph or null if commit-graph file does not exist or
+	 *         corrupt.
+	 */
+	CommitGraph get() {
+		GraphSnapshot original = baseGraph.get();
+		synchronized (baseGraph) {
+			GraphSnapshot o, n;
+			do {
+				o = baseGraph.get();
+				if (o != original) {
+					// Another thread did the scan for us, while we
+					// were blocked on the monitor above.
+					//
+					return o.getCommitGraph();
+				}
+				n = o.refresh();
+				if (n == o) {
+					return n.getCommitGraph();
+				}
+			} while (!baseGraph.compareAndSet(o, n));
+			return n.getCommitGraph();
+		}
+	}
+
+	private static final class GraphSnapshot {
+		private final File file;
+
+		private final FileSnapshot snapshot;
+
+		private final CommitGraph graph;
+
+		GraphSnapshot(@NonNull File file) {
+			this(file, null, null);
+		}
+
+		GraphSnapshot(@NonNull File file, FileSnapshot snapshot,
+				CommitGraph graph) {
+			this.file = file;
+			this.snapshot = snapshot;
+			this.graph = graph;
+		}
+
+		CommitGraph getCommitGraph() {
+			return graph;
+		}
+
+		GraphSnapshot refresh() {
+			if (graph == null && !file.exists()) {
+				// commit-graph file didn't exist
+				return this;
+			}
+			if (snapshot != null && !snapshot.isModified(file)) {
+				// commit-graph file was not modified
+				return this;
+			}
+			return new GraphSnapshot(file, FileSnapshot.save(file), open(file));
+		}
+
+		private static CommitGraph open(File file) {
+			try {
+				return CommitGraphLoader.open(file);
+			} catch (FileNotFoundException noFile) {
+				// ignore if file do not exist
+				return null;
+			} catch (IOException e) {
+				if (e instanceof CommitGraphFormatException) {
+					LOG.warn(
+							MessageFormat.format(
+									JGitText.get().corruptCommitGraph, file),
+							e);
+				} else {
+					LOG.error(MessageFormat.format(
+							JGitText.get().exceptionWhileLoadingCommitGraph,
+							file), e);
+				}
+				return null;
+			}
+		}
+	}
+}
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 e97ed39..aa578d3 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
@@ -13,8 +13,10 @@
 import java.io.File;
 import java.io.IOException;
 import java.util.Collection;
+import java.util.Optional;
 import java.util.Set;
 
+import org.eclipse.jgit.internal.storage.commitgraph.CommitGraph;
 import org.eclipse.jgit.internal.storage.pack.ObjectToPack;
 import org.eclipse.jgit.internal.storage.pack.PackWriter;
 import org.eclipse.jgit.lib.AbbreviatedObjectId;
@@ -72,4 +74,6 @@
 	abstract Pack openPack(File pack) throws IOException;
 
 	abstract Collection<Pack> getPacks();
+
+	abstract Optional<CommitGraph> getCommitGraph();
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java
index 799d058..06ec80c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java
@@ -14,6 +14,7 @@
 import static org.eclipse.jgit.internal.storage.pack.PackExt.INDEX;
 import static org.eclipse.jgit.internal.storage.pack.PackExt.KEEP;
 import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
+import static org.eclipse.jgit.internal.storage.pack.PackExt.REVERSE_INDEX;
 
 import java.io.File;
 import java.io.FileOutputStream;
@@ -69,10 +70,13 @@
 import org.eclipse.jgit.errors.MissingObjectException;
 import org.eclipse.jgit.errors.NoWorkTreeException;
 import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.commitgraph.CommitGraphWriter;
+import org.eclipse.jgit.internal.storage.commitgraph.GraphCommits;
 import org.eclipse.jgit.internal.storage.pack.PackExt;
 import org.eclipse.jgit.internal.storage.pack.PackWriter;
 import org.eclipse.jgit.lib.ConfigConstants;
 import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.CoreConfig;
 import org.eclipse.jgit.lib.FileMode;
 import org.eclipse.jgit.lib.NullProgressMonitor;
 import org.eclipse.jgit.lib.ObjectId;
@@ -121,19 +125,17 @@
 	private static final Pattern PATTERN_LOOSE_OBJECT = Pattern
 			.compile("[0-9a-fA-F]{38}"); //$NON-NLS-1$
 
-	private static final String PACK_EXT = "." + PackExt.PACK.getExtension();//$NON-NLS-1$
+	private static final Set<PackExt> PARENT_EXTS = Set.of(PACK, KEEP);
 
-	private static final String BITMAP_EXT = "." //$NON-NLS-1$
-			+ PackExt.BITMAP_INDEX.getExtension();
-
-	private static final String INDEX_EXT = "." + PackExt.INDEX.getExtension(); //$NON-NLS-1$
-
-	private static final String KEEP_EXT = "." + PackExt.KEEP.getExtension(); //$NON-NLS-1$
+	private static final Set<PackExt> CHILD_EXTS = Set.of(BITMAP_INDEX, INDEX,
+			REVERSE_INDEX);
 
 	private static final int DEFAULT_AUTOPACKLIMIT = 50;
 
 	private static final int DEFAULT_AUTOLIMIT = 6700;
 
+	private static final boolean DEFAULT_WRITE_COMMIT_GRAPH = false;
+
 	private static volatile ExecutorService executor;
 
 	/**
@@ -285,6 +287,9 @@
 			Collection<Pack> newPacks = repack();
 			prune(Collections.emptySet());
 			// TODO: implement rerere_gc(pm);
+			if (shouldWriteCommitGraphWhenGc()) {
+				writeCommitGraph(refsToObjectIds(getAllRefs()));
+			}
 			return newPacks;
 		}
 	}
@@ -902,6 +907,105 @@
 		return ret;
 	}
 
+	private Set<ObjectId> refsToObjectIds(Collection<Ref> refs)
+			throws IOException {
+		Set<ObjectId> objectIds = new HashSet<>();
+		for (Ref ref : refs) {
+			checkCancelled();
+			if (ref.getPeeledObjectId() != null) {
+				objectIds.add(ref.getPeeledObjectId());
+				continue;
+			}
+
+			if (ref.getObjectId() != null) {
+				objectIds.add(ref.getObjectId());
+			}
+		}
+		return objectIds;
+	}
+
+	/**
+	 * Generate a new commit-graph file when 'core.commitGraph' is true.
+	 *
+	 * @param wants
+	 *            the list of wanted objects, writer walks commits starting at
+	 *            these. Must not be {@code null}.
+	 * @throws IOException
+	 */
+	void writeCommitGraph(@NonNull Set<? extends ObjectId> wants)
+			throws IOException {
+		if (!repo.getConfig().get(CoreConfig.KEY).enableCommitGraph()) {
+			return;
+		}
+		if (repo.getObjectDatabase().getShallowCommits().size() > 0) {
+			return;
+		}
+		checkCancelled();
+		if (wants.isEmpty()) {
+			return;
+		}
+		File tmpFile = null;
+		try (RevWalk walk = new RevWalk(repo)) {
+			CommitGraphWriter writer = new CommitGraphWriter(
+					GraphCommits.fromWalk(pm, wants, walk));
+			tmpFile = File.createTempFile("commit_", ".graph_tmp", //$NON-NLS-1$//$NON-NLS-2$
+					repo.getObjectDatabase().getInfoDirectory());
+			// write the commit-graph file
+			try (FileOutputStream fos = new FileOutputStream(tmpFile);
+					FileChannel channel = fos.getChannel();
+					OutputStream channelStream = Channels
+							.newOutputStream(channel)) {
+				writer.write(pm, channelStream);
+				channel.force(true);
+			}
+
+			// rename the temporary file to real file
+			File realFile = new File(repo.getObjectsDirectory(),
+					Constants.INFO_COMMIT_GRAPH);
+			FileUtils.rename(tmpFile, realFile, StandardCopyOption.ATOMIC_MOVE);
+		} finally {
+			if (tmpFile != null && tmpFile.exists()) {
+				tmpFile.delete();
+			}
+		}
+		deleteTempCommitGraph();
+	}
+
+	private void deleteTempCommitGraph() {
+		Path objectsDir = repo.getObjectDatabase().getInfoDirectory().toPath();
+		Instant threshold = Instant.now().minus(1, ChronoUnit.DAYS);
+		if (!Files.exists(objectsDir)) {
+			return;
+		}
+		try (DirectoryStream<Path> stream = Files.newDirectoryStream(objectsDir,
+				"commit_*_tmp")) { //$NON-NLS-1$
+			stream.forEach(t -> {
+				try {
+					Instant lastModified = Files.getLastModifiedTime(t)
+							.toInstant();
+					if (lastModified.isBefore(threshold)) {
+						Files.deleteIfExists(t);
+					}
+				} catch (IOException e) {
+					LOG.error(e.getMessage(), e);
+				}
+			});
+		} catch (IOException e) {
+			LOG.error(e.getMessage(), e);
+		}
+	}
+
+	/**
+	 * If {@code true}, will rewrite the commit-graph file when gc is run.
+	 *
+	 * @return true if commit-graph should be writen. Default is {@code false}.
+	 */
+	boolean shouldWriteCommitGraphWhenGc() {
+		return repo.getConfig().getBoolean(ConfigConstants.CONFIG_GC_SECTION,
+				ConfigConstants.CONFIG_KEY_WRITE_COMMIT_GRAPH,
+				DEFAULT_WRITE_COMMIT_GRAPH);
+	}
+
 	private static boolean isHead(Ref ref) {
 		return ref.getName().startsWith(Constants.R_HEADS);
 	}
@@ -962,47 +1066,52 @@
 		}
 	}
 
+	private static Optional<PackFile> toPackFileWithValidExt(
+			Path packFilePath) {
+		try {
+			PackFile packFile = new PackFile(packFilePath.toFile());
+			if (packFile.getPackExt() == null) {
+				return Optional.empty();
+			}
+			return Optional.of(packFile);
+		} catch (IllegalArgumentException e) {
+			return Optional.empty();
+		}
+	}
+
 	/**
 	 * Deletes orphans
 	 * <p>
-	 * A file is considered an orphan if it is either a "bitmap" or an index
-	 * file, and its corresponding pack file is missing in the list.
+	 * A file is considered an orphan if it is some type of index file, but
+	 * there is not a corresponding pack or keep file present in the directory.
 	 * </p>
 	 */
 	private void deleteOrphans() {
 		Path packDir = repo.getObjectDatabase().getPackDirectory().toPath();
-		List<String> fileNames = null;
+		List<PackFile> childFiles;
+		Set<String> seenParentIds = new HashSet<>();
 		try (Stream<Path> files = Files.list(packDir)) {
-			fileNames = files.map(path -> path.getFileName().toString())
-					.filter(name -> (name.endsWith(PACK_EXT)
-							|| name.endsWith(BITMAP_EXT)
-							|| name.endsWith(INDEX_EXT)
-							|| name.endsWith(KEEP_EXT)))
-					// sort files with same base name in the order:
-					// .pack, .keep, .index, .bitmap to avoid look ahead
-					.sorted(Collections.reverseOrder())
-					.collect(Collectors.toList());
+			childFiles = files.map(GC::toPackFileWithValidExt)
+					.filter(Optional::isPresent).map(Optional::get)
+					.filter(packFile -> {
+						PackExt ext = packFile.getPackExt();
+						if (PARENT_EXTS.contains(ext)) {
+							seenParentIds.add(packFile.getId());
+							return false;
+						}
+						return CHILD_EXTS.contains(ext);
+					}).collect(Collectors.toList());
 		} catch (IOException e) {
 			LOG.error(e.getMessage(), e);
 			return;
 		}
-		if (fileNames == null) {
-			return;
-		}
 
-		String latestId = null;
-		for (String n : fileNames) {
-			PackFile pf = new PackFile(packDir.toFile(), n);
-			PackExt ext = pf.getPackExt();
-			if (ext.equals(PACK) || ext.equals(KEEP)) {
-				latestId = pf.getId();
-			}
-			if (latestId == null || !pf.getId().equals(latestId)) {
-				// no pack or keep for this id
+		for (PackFile child : childFiles) {
+			if (!seenParentIds.contains(child.getId())) {
 				try {
-					FileUtils.delete(pf,
+					FileUtils.delete(child,
 							FileUtils.RETRY | FileUtils.SKIP_MISSING);
-					LOG.warn(JGitText.get().deletedOrphanInPackDir, pf);
+					LOG.warn(JGitText.get().deletedOrphanInPackDir, child);
 				} catch (IOException e) {
 					LOG.error(e.getMessage(), e);
 				}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LooseObjects.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LooseObjects.java
index b9af83d..326c5f6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LooseObjects.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LooseObjects.java
@@ -254,7 +254,7 @@
 					// refresh directory to work around NFS caching issue
 				}
 				return getSizeWithoutRefresh(curs, id);
-			} catch (FileNotFoundException e) {
+			} catch (FileNotFoundException unused) {
 				if (fileFor(id).exists()) {
 					throw noFile;
 				}
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 53fdc66..f27daad 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
@@ -28,6 +28,7 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicReference;
 
@@ -38,8 +39,10 @@
 import org.eclipse.jgit.internal.storage.pack.PackWriter;
 import org.eclipse.jgit.lib.AbbreviatedObjectId;
 import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.internal.storage.commitgraph.CommitGraph;
 import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.CoreConfig;
 import org.eclipse.jgit.lib.ObjectDatabase;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectLoader;
@@ -86,6 +89,8 @@
 
 	private final File alternatesFile;
 
+	private final FileCommitGraph fileCommitGraph;
+
 	private final FS fs;
 
 	private final AtomicReference<AlternateHandle[]> alternates;
@@ -125,6 +130,7 @@
 		loose = new LooseObjects(config, objects);
 		packed = new PackDirectory(config, packDirectory);
 		preserved = new PackDirectory(config, preservedDirectory);
+		fileCommitGraph = new FileCommitGraph(objects);
 		this.fs = fs;
 		this.shallowFile = shallowFile;
 
@@ -228,6 +234,15 @@
 		return count;
 	}
 
+	/** {@inheritDoc} */
+	@Override
+	public Optional<CommitGraph> getCommitGraph() {
+		if (config.get(CoreConfig.KEY).enableCommitGraph()) {
+			return Optional.ofNullable(fileCommitGraph.get());
+		}
+		return Optional.empty();
+	}
+
 	/**
 	 * {@inheritDoc}
 	 * <p>
@@ -809,4 +824,8 @@
 	AlternateHandle.Id getAlternateId() {
 		return handle.getId();
 	}
+
+	File getInfoDirectory() {
+		return infoDirectory;
+	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexV1.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexV1.java
index 21aba3e..988dc6c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexV1.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexV1.java
@@ -220,7 +220,7 @@
 		long offset = packIndex.findOffset(objectId);
 		if (offset == -1)
 			return -1;
-		return reverseIndex.findPostion(offset);
+		return reverseIndex.findPosition(offset);
 	}
 
 	/** {@inheritDoc} */
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndex.java
index 942cc96..f4f62d4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndex.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndex.java
@@ -241,6 +241,17 @@
 	public abstract long findOffset(AnyObjectId objId);
 
 	/**
+	 * Locate the position of this id in the list of object-ids in the index
+	 *
+	 * @param objId
+	 *            name of the object to locate within the index
+	 * @return position of the object-id in the lexicographically ordered list
+	 *         of ids stored in this index; -1 if the object does not exist in
+	 *         this index and is thus not stored in the associated pack.
+	 */
+	public abstract int findPosition(AnyObjectId objId);
+
+	/**
 	 * Retrieve stored CRC32 checksum of the requested object raw-data
 	 * (including header).
 	 *
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV1.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV1.java
index eb0ac6a..fff410b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV1.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV1.java
@@ -32,6 +32,8 @@
 class PackIndexV1 extends PackIndex {
 	private static final int IDX_HDR_LEN = 256 * 4;
 
+	private static final int RECORD_SIZE = 4 + Constants.OBJECT_ID_LENGTH;
+
 	private final long[] idxHeader;
 
 	byte[][] idxdata;
@@ -131,8 +133,50 @@
 	public long findOffset(AnyObjectId objId) {
 		final int levelOne = objId.getFirstByte();
 		byte[] data = idxdata[levelOne];
-		if (data == null)
+		int pos = levelTwoPosition(objId, data);
+		if (pos < 0) {
 			return -1;
+		}
+		// The records are (offset, objectid), pos points to objectId
+		int b0 = data[pos - 4] & 0xff;
+		int b1 = data[pos - 3] & 0xff;
+		int b2 = data[pos - 2] & 0xff;
+		int b3 = data[pos - 1] & 0xff;
+		return (((long) b0) << 24) | (b1 << 16) | (b2 << 8) | (b3);
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public int findPosition(AnyObjectId objId) {
+		int levelOne = objId.getFirstByte();
+		int levelTwo = levelTwoPosition(objId, idxdata[levelOne]);
+		if (levelTwo < 0) {
+			return -1;
+		}
+		long objsBefore = levelOne == 0 ? 0 : idxHeader[levelOne - 1];
+		return (int) objsBefore + ((levelTwo - 4) / RECORD_SIZE);
+	}
+
+	/**
+	 * Find position in level two data of this objectId
+	 *
+	 * Records are (offset, objectId), so to read the corresponding offset,
+	 * caller must substract from this position.
+	 *
+	 * @param objId
+	 *            ObjectId we are looking for
+	 * @param data
+	 *            Blob of second level data with a series of (offset, objectid)
+	 *            pairs where we should find objId
+	 *
+	 * @return position in the byte[] where the objectId starts. -1 if not
+	 *         found.
+	 */
+	private int levelTwoPosition(AnyObjectId objId, byte[] data) {
+		if (data == null || data.length == 0) {
+			return -1;
+		}
+
 		int high = data.length / (4 + Constants.OBJECT_ID_LENGTH);
 		int low = 0;
 		do {
@@ -142,11 +186,7 @@
 			if (cmp < 0)
 				high = mid;
 			else if (cmp == 0) {
-				int b0 = data[pos - 4] & 0xff;
-				int b1 = data[pos - 3] & 0xff;
-				int b2 = data[pos - 2] & 0xff;
-				int b3 = data[pos - 1] & 0xff;
-				return (((long) b0) << 24) | (b1 << 16) | (b2 << 8) | (b3);
+				return pos;
 			} else
 				low = mid + 1;
 		} while (low < high);
@@ -204,7 +244,7 @@
 	}
 
 	private static int idOffset(int mid) {
-		return ((4 + Constants.OBJECT_ID_LENGTH) * mid) + 4;
+		return (RECORD_SIZE * mid) + 4;
 	}
 
 	private class IndexV1Iterator extends EntriesIterator {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV2.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV2.java
index 09397e3..7a39006 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV2.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV2.java
@@ -192,6 +192,18 @@
 		return getOffset(levelOne, levelTwo);
 	}
 
+	/** {@inheritDoc} */
+	@Override
+	public int findPosition(AnyObjectId objId) {
+		int levelOne = objId.getFirstByte();
+		int levelTwo = binarySearchLevelTwo(objId, levelOne);
+		if (levelTwo < 0) {
+			return -1;
+		}
+		long objsBefore = levelOne == 0 ? 0 : fanoutTable[levelOne - 1];
+		return (int) objsBefore + levelTwo;
+	}
+
 	private long getOffset(int levelOne, int levelTwo) {
 		final long p = NB.decodeUInt32(offset32[levelOne], levelTwo << 2);
 		if ((p & IS_O64) != 0)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackObjectSizeIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackObjectSizeIndex.java
new file mode 100644
index 0000000..1c3797c
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackObjectSizeIndex.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 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
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.storage.file;
+
+/**
+ * Index of object sizes in a pack
+ *
+ * It is not guaranteed that the implementation contains the sizes of all
+ * objects (e.g. it could store only objects over certain threshold).
+ */
+public interface PackObjectSizeIndex {
+
+	/**
+	 * Returns the inflated size of the object.
+	 *
+	 * @param idxOffset
+	 *            position in the pack (as returned from PackIndex)
+	 * @return size of the object, -1 if not found in the index.
+	 */
+	long getSize(int idxOffset);
+
+	/**
+	 * Number of objects in the index
+	 *
+	 * @return number of objects in the index
+	 */
+	long getObjectCount();
+
+
+	/**
+	 * Minimal size of an object to be included in this index
+	 *
+	 * Cut-off value used at generation time to decide what objects to index.
+	 *
+	 * @return size in bytes
+	 */
+	int getThreshold();
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackObjectSizeIndexLoader.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackObjectSizeIndexLoader.java
new file mode 100644
index 0000000..9d69418
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackObjectSizeIndexLoader.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 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
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.storage.file;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+
+/**
+ * Chooses the specific implementation of the object-size index based on the
+ * file version.
+ */
+public class PackObjectSizeIndexLoader {
+
+	/**
+	 * Read an object size index from the stream
+	 *
+	 * @param in
+	 *            input stream at the beginning of the object size data
+	 * @return an implementation of the object size index
+	 * @throws IOException
+	 *             error reading the streams
+	 */
+	public static PackObjectSizeIndex load(InputStream in) throws IOException {
+		byte[] header = in.readNBytes(4);
+		if (!Arrays.equals(header, PackObjectSizeIndexWriter.HEADER)) {
+			throw new IOException("Stream is not an object index"); //$NON-NLS-1$
+		}
+
+		int version = in.readNBytes(1)[0];
+		if (version != 1) {
+			throw new IOException("Unknown object size version: " + version); //$NON-NLS-1$
+		}
+		return PackObjectSizeIndexV1.parse(in);
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackObjectSizeIndexV1.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackObjectSizeIndexV1.java
new file mode 100644
index 0000000..be2ff67
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackObjectSizeIndexV1.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 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
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.storage.file;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.Arrays;
+
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.util.NB;
+
+/**
+ * Memory representation of the object-size index
+ *
+ * The object size index is a map from position in the primary idx (i.e.
+ * position of the object-id in lexicographical order) to size.
+ *
+ * Most of the positions fit in unsigned 3 bytes (up to 16 million)
+ */
+class PackObjectSizeIndexV1 implements PackObjectSizeIndex {
+
+	private static final byte BITS_24 = 0x18;
+
+	private static final byte BITS_32 = 0x20;
+
+	private final int threshold;
+
+	private final UInt24Array positions24;
+
+	private final int[] positions32;
+
+	/**
+	 * Parallel array to concat(positions24, positions32) with the size of the
+	 * objects.
+	 *
+	 * A value >= 0 is the size of the object. A negative value means the size
+	 * doesn't fit in an int and |value|-1 is the position for the size in the
+	 * size64 array e.g. a value of -1 is sizes64[0], -2 = sizes64[1], ...
+	 */
+	private final int[] sizes32;
+
+	private final long[] sizes64;
+
+	static PackObjectSizeIndex parse(InputStream in) throws IOException {
+		/** Header and version already out of the input */
+		IndexInputStreamReader stream = new IndexInputStreamReader(in);
+		int threshold = stream.readInt(); // minSize
+		int objCount = stream.readInt();
+		if (objCount == 0) {
+			return new EmptyPackObjectSizeIndex(threshold);
+		}
+		return new PackObjectSizeIndexV1(stream, threshold, objCount);
+	}
+
+	private PackObjectSizeIndexV1(IndexInputStreamReader stream, int threshold,
+			int objCount) throws IOException {
+		this.threshold = threshold;
+		UInt24Array pos24 = null;
+		int[] pos32 = null;
+
+		byte positionEncoding;
+		while ((positionEncoding = stream.readByte()) != 0) {
+			if (Byte.compareUnsigned(positionEncoding, BITS_24) == 0) {
+				int sz = stream.readInt();
+				pos24 = new UInt24Array(stream.readNBytes(sz * 3));
+			} else if (Byte.compareUnsigned(positionEncoding, BITS_32) == 0) {
+				int sz = stream.readInt();
+				pos32 = stream.readIntArray(sz);
+			} else {
+				throw new UnsupportedEncodingException(
+						String.format(JGitText.get().unknownPositionEncoding,
+								Integer.toHexString(positionEncoding)));
+			}
+		}
+		positions24 = pos24 != null ? pos24 : UInt24Array.EMPTY;
+		positions32 = pos32 != null ? pos32 : new int[0];
+
+		sizes32 = stream.readIntArray(objCount);
+		int c64sizes = stream.readInt();
+		if (c64sizes == 0) {
+			sizes64 = new long[0];
+			return;
+		}
+		sizes64 = stream.readLongArray(c64sizes);
+		int c128sizes = stream.readInt();
+		if (c128sizes != 0) {
+			// this MUST be 0 (we don't support 128 bits sizes yet)
+			throw new IOException(JGitText.get().unsupportedSizesObjSizeIndex);
+		}
+	}
+
+	@Override
+	public long getSize(int idxOffset) {
+		int pos = -1;
+		if (!positions24.isEmpty() && idxOffset <= positions24.getLastValue()) {
+			pos = positions24.binarySearch(idxOffset);
+		} else if (positions32.length > 0 && idxOffset >= positions32[0]) {
+			int pos32 = Arrays.binarySearch(positions32, idxOffset);
+			if (pos32 >= 0) {
+				pos = pos32 + positions24.size();
+			}
+		}
+		if (pos < 0) {
+			return -1;
+		}
+
+		int objSize = sizes32[pos];
+		if (objSize < 0) {
+			int secondPos = Math.abs(objSize) - 1;
+			return sizes64[secondPos];
+		}
+		return objSize;
+	}
+
+	@Override
+	public long getObjectCount() {
+		return positions24.size() + positions32.length;
+	}
+
+	@Override
+	public int getThreshold() {
+		return threshold;
+	}
+
+	/**
+	 * Wrapper to read parsed content from the byte stream
+	 */
+	private static class IndexInputStreamReader {
+
+		private final byte[] buffer = new byte[8];
+
+		private final InputStream in;
+
+		IndexInputStreamReader(InputStream in) {
+			this.in = in;
+		}
+
+		int readInt() throws IOException {
+			int n = in.readNBytes(buffer, 0, 4);
+			if (n < 4) {
+				throw new IOException(JGitText.get().unableToReadFullInt);
+			}
+			return NB.decodeInt32(buffer, 0);
+		}
+
+		int[] readIntArray(int intsCount) throws IOException {
+			if (intsCount == 0) {
+				return new int[0];
+			}
+
+			int[] dest = new int[intsCount];
+			for (int i = 0; i < intsCount; i++) {
+				dest[i] = readInt();
+			}
+			return dest;
+		}
+
+		long readLong() throws IOException {
+			int n = in.readNBytes(buffer, 0, 8);
+			if (n < 8) {
+				throw new IOException(JGitText.get().unableToReadFullInt);
+			}
+			return NB.decodeInt64(buffer, 0);
+		}
+
+		long[] readLongArray(int longsCount) throws IOException {
+			if (longsCount == 0) {
+				return new long[0];
+			}
+
+			long[] dest = new long[longsCount];
+			for (int i = 0; i < longsCount; i++) {
+				dest[i] = readLong();
+			}
+			return dest;
+		}
+
+		byte readByte() throws IOException {
+			int n = in.readNBytes(buffer, 0, 1);
+			if (n != 1) {
+				throw new IOException(JGitText.get().cannotReadByte);
+			}
+			return buffer[0];
+		}
+
+		byte[] readNBytes(int sz) throws IOException {
+			return in.readNBytes(sz);
+		}
+	}
+
+	private static class EmptyPackObjectSizeIndex
+			implements PackObjectSizeIndex {
+
+		private final int threshold;
+
+		EmptyPackObjectSizeIndex(int threshold) {
+			this.threshold = threshold;
+		}
+
+		@Override
+		public long getSize(int idxOffset) {
+			return -1;
+		}
+
+		@Override
+		public long getObjectCount() {
+			return 0;
+		}
+
+		@Override
+		public int getThreshold() {
+			return threshold;
+		}
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackObjectSizeIndexWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackObjectSizeIndexWriter.java
new file mode 100644
index 0000000..65a065d
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackObjectSizeIndexWriter.java
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 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
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.storage.file;
+
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.List;
+
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.transport.PackedObjectInfo;
+import org.eclipse.jgit.util.NB;
+
+/**
+ * Write an object index in the output stream
+ */
+public abstract class PackObjectSizeIndexWriter {
+
+	private static final int MAX_24BITS_UINT = 0xffffff;
+
+	private static final PackObjectSizeIndexWriter NULL_WRITER = new PackObjectSizeIndexWriter() {
+		@Override
+		public void write(List<? extends PackedObjectInfo> objs) {
+			// Do nothing
+		}
+	};
+
+	/** Magic constant for the object size index file */
+	protected static final byte[] HEADER = { -1, 's', 'i', 'z' };
+
+	/**
+	 * Returns a writer for the latest index version
+	 *
+	 * @param os
+	 *            Output stream where to write the index
+	 * @param minSize
+	 *            objects strictly smaller than this size won't be added to the
+	 *            index. Negative size won't write AT ALL. Other sizes could write
+	 *            an empty index.
+	 * @return the index writer
+	 */
+	public static PackObjectSizeIndexWriter createWriter(OutputStream os,
+			int minSize) {
+		if (minSize < 0) {
+			return NULL_WRITER;
+		}
+		return new PackObjectSizeWriterV1(os, minSize);
+	}
+
+	/**
+	 * Add the objects to the index
+	 *
+	 * @param objs
+	 *            objects in the pack, in sha1 order. Their position in the list
+	 *            matches their position in the primary index.
+	 * @throws IOException
+	 *             problem writing to the stream
+	 */
+	public abstract void write(List<? extends PackedObjectInfo> objs)
+			throws IOException;
+
+	/**
+	 * Object size index v1.
+	 *
+	 * Store position (in the main index) to size as parallel arrays.
+	 *
+	 * <p>Positions in the main index fit well in unsigned 24 bits (16M) for most
+	 * repositories, but some outliers have even more objects, so we need to
+	 * store also 32 bits positions.
+	 *
+	 * <p>Sizes are stored as a first array parallel to positions. If a size
+	 * doesn't fit in an element of that array, then we encode there a position
+	 * on the next-size array. This "overflow" array doesn't have entries for
+	 * all positions.
+	 *
+	 * <pre>
+	 *
+	 *      positions       [10, 500, 1000, 1001]
+	 *      sizes (32bits)  [15MB, -1, 6MB, -2]
+   *                          ___/  ______/
+	 *                        /     /
+	 *      sizes (64 bits) [3GB, 6GB]
+	 * </pre>
+	 *
+	 * <p>For sizes we use 32 bits as the first level and 64 for the rare objects
+	 * over 2GB.
+	 *
+	 * <p>A 24/32/64 bits hierarchy of arrays saves space if we have a lot of small
+	 * objects, but wastes space if we have only big ones. The min size to index is
+	 * controlled by conf and in principle we want to index only rather
+	 * big objects (e.g. > 10MB). We could support more dynamics read/write of sizes
+	 * (e.g. 24 only if the threshold will include many of those objects) but it
+	 * complicates a lot code and spec. If needed it could go for a v2 of the protocol.
+	 *
+	 * <p>Format:
+	 *
+	 * <li>A header with the magic number (4 bytes)
+	 * <li>The index version (1 byte)
+	 * <li>The minimum object size (4 bytes)
+	 * <li>Total count of objects indexed (C, 4 bytes)
+	 * (if count == 0, stop here)
+	 *
+	 * Blocks of
+	 * <li>Size per entry in bits (1 byte, either 24 (0x18) or 32 (0x20))
+	 * <li>Count of entries (4 bytes) (c, as a signed int)
+	 * <li>positions encoded in s bytes each (i.e s*c bytes)
+	 *
+	 * <li>0 (as a "size-per-entry = 0", marking end of the section)
+	 *
+	 * <li>32 bit sizes (C * 4 bytes). Negative size means
+	 * nextLevel[abs(size)-1]
+	 * <li>Count of 64 bit sizes (s64) (or 0 if no more indirections)
+	 * <li>64 bit sizes (s64 * 8 bytes)
+	 * <li>0 (end)
+	 */
+	static class PackObjectSizeWriterV1 extends PackObjectSizeIndexWriter {
+
+		private final OutputStream os;
+
+		private final int minObjSize;
+
+		private final byte[] intBuffer = new byte[4];
+
+		PackObjectSizeWriterV1(OutputStream os, int minSize) {
+			this.os = new BufferedOutputStream(os);
+			this.minObjSize = minSize;
+		}
+
+		@Override
+		public void write(List<? extends PackedObjectInfo> allObjects)
+				throws IOException {
+			os.write(HEADER);
+			writeUInt8(1); // Version
+			writeInt32(minObjSize);
+
+			PackedObjectStats stats = countIndexableObjects(allObjects);
+			int[] indexablePositions = findIndexablePositions(allObjects,
+					stats.indexableObjs);
+			writeInt32(indexablePositions.length); // Total # of objects
+			if (indexablePositions.length == 0) {
+				os.flush();
+				return;
+			}
+
+			// Positions that fit in 3 bytes
+			if (stats.pos24Bits > 0) {
+				writeUInt8(24);
+				writeInt32(stats.pos24Bits);
+				applyToRange(indexablePositions, 0, stats.pos24Bits,
+						this::writeInt24);
+			}
+			// Positions that fit in 4 bytes
+			// We only use 31 bits due to sign,
+			// but that covers 2 billion objs
+			if (stats.pos31Bits > 0) {
+				writeUInt8(32);
+				writeInt32(stats.pos31Bits);
+				applyToRange(indexablePositions, stats.pos24Bits,
+						stats.pos24Bits + stats.pos31Bits, this::writeInt32);
+			}
+			writeUInt8(0);
+			writeSizes(allObjects, indexablePositions, stats.sizeOver2GB);
+			os.flush();
+		}
+
+		private void writeUInt8(int i) throws IOException {
+			if (i > 255) {
+				throw new IllegalStateException(
+						JGitText.get().numberDoesntFit);
+			}
+			NB.encodeInt32(intBuffer, 0, i);
+			os.write(intBuffer, 3, 1);
+		}
+
+		private void writeInt24(int i) throws IOException {
+			NB.encodeInt24(intBuffer, 1, i);
+			os.write(intBuffer, 1, 3);
+		}
+
+		private void writeInt32(int i) throws IOException {
+			NB.encodeInt32(intBuffer, 0, i);
+			os.write(intBuffer);
+		}
+
+		private void writeSizes(List<? extends PackedObjectInfo> allObjects,
+				int[] indexablePositions, int objsBiggerThan2Gb)
+				throws IOException {
+			if (indexablePositions.length == 0) {
+				writeInt32(0);
+				return;
+			}
+
+			byte[] sizes64bits = new byte[8 * objsBiggerThan2Gb];
+			int s64 = 0;
+			for (int i = 0; i < indexablePositions.length; i++) {
+				PackedObjectInfo info = allObjects.get(indexablePositions[i]);
+				if (info.getFullSize() < Integer.MAX_VALUE) {
+					writeInt32((int) info.getFullSize());
+				} else {
+					// Size needs more than 32 bits. Store -1 * offset in the
+					// next table as size.
+					writeInt32(-1 * (s64 + 1));
+					NB.encodeInt64(sizes64bits, s64 * 8, info.getFullSize());
+					s64++;
+				}
+			}
+			if (objsBiggerThan2Gb > 0) {
+				writeInt32(objsBiggerThan2Gb);
+				os.write(sizes64bits);
+			}
+			writeInt32(0);
+		}
+
+		private int[] findIndexablePositions(
+				List<? extends PackedObjectInfo> allObjects,
+				int indexableObjs) {
+			int[] positions = new int[indexableObjs];
+			int positionIdx = 0;
+			for (int i = 0; i < allObjects.size(); i++) {
+				PackedObjectInfo o = allObjects.get(i);
+				if (!shouldIndex(o)) {
+					continue;
+				}
+				positions[positionIdx++] = i;
+			}
+			return positions;
+		}
+
+		private PackedObjectStats countIndexableObjects(
+				List<? extends PackedObjectInfo> objs) {
+			PackedObjectStats stats = new PackedObjectStats();
+			for (int i = 0; i < objs.size(); i++) {
+				PackedObjectInfo o = objs.get(i);
+				if (!shouldIndex(o)) {
+					continue;
+				}
+				stats.indexableObjs++;
+				if (o.getFullSize() > Integer.MAX_VALUE) {
+					stats.sizeOver2GB++;
+				}
+				if (i <= MAX_24BITS_UINT) {
+					stats.pos24Bits++;
+				} else {
+					stats.pos31Bits++;
+					// i is a positive int, cannot be bigger than this
+				}
+			}
+			return stats;
+		}
+
+		private boolean shouldIndex(PackedObjectInfo o) {
+			return (o.getType() == Constants.OBJ_BLOB)
+					&& (o.getFullSize() >= minObjSize);
+		}
+
+		private static class PackedObjectStats {
+			int indexableObjs;
+
+			int pos24Bits;
+
+			int pos31Bits;
+
+			int sizeOver2GB;
+		}
+
+		@FunctionalInterface
+		interface IntEncoder {
+			void encode(int i) throws IOException;
+		}
+
+		private static void applyToRange(int[] allPositions, int start, int end,
+				IntEncoder encoder) throws IOException {
+			for (int i = start; i < end; i++) {
+				encoder.encode(allPositions[i]);
+			}
+		}
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackReverseIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackReverseIndex.java
index ee458e2..1a5adb4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackReverseIndex.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackReverseIndex.java
@@ -158,7 +158,7 @@
 		return index.getOffset(nth[ith + 1]);
 	}
 
-	int findPostion(long offset) {
+	int findPosition(long offset) {
 		return binarySearch(offset);
 	}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java
index d72f935..e9abb02 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java
@@ -112,11 +112,6 @@
 	/** If in the header, denotes the file has peeled data. */
 	public static final String PACKED_REFS_PEELED = " peeled"; //$NON-NLS-1$
 
-	/** The names of the additional refs supported by this class */
-	private static final String[] additionalRefsNames = new String[] {
-			Constants.MERGE_HEAD, Constants.FETCH_HEAD, Constants.ORIG_HEAD,
-			Constants.CHERRY_PICK_HEAD };
-
 	@SuppressWarnings("boxing")
 	private static final List<Integer> RETRY_SLEEP_MS =
 			Collections.unmodifiableList(Arrays.asList(0, 100, 200, 400, 800, 1600));
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/UInt24Array.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/UInt24Array.java
new file mode 100644
index 0000000..3a0a18b
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/UInt24Array.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2023, Google LLC
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.storage.file;
+
+/**
+ * A view of a byte[] as a list of integers stored in 3 bytes.
+ *
+ * The ints are stored in big-endian ("network order"), so
+ * byte[]{aa,bb,cc} becomes the int 0x00aabbcc
+ */
+final class UInt24Array {
+
+	public static final UInt24Array EMPTY = new UInt24Array(
+			new byte[0]);
+
+	private static final int ENTRY_SZ = 3;
+
+	private final byte[] data;
+
+	private final int size;
+
+	UInt24Array(byte[] data) {
+		this.data = data;
+		this.size = data.length / ENTRY_SZ;
+	}
+
+	boolean isEmpty() {
+		return size == 0;
+	}
+
+	int size() {
+		return size;
+	}
+
+	int get(int index) {
+		if (index < 0 || index >= size) {
+			throw new IndexOutOfBoundsException(index);
+		}
+		int offset = index * ENTRY_SZ;
+		int e = data[offset] & 0xff;
+		e <<= 8;
+		e |= data[offset + 1] & 0xff;
+		e <<= 8;
+		e |= data[offset + 2] & 0xff;
+		return e;
+	}
+
+	/**
+	 * Search needle in the array.
+	 *
+	 * This assumes a sorted array.
+	 *
+	 * @param needle
+	 *            It cannot be bigger than 0xffffff (max unsigned three bytes).
+	 * @return position of the needle in the array, -1 if not found. Runtime
+	 *         exception if the value is too big for 3 bytes.
+	 */
+	int binarySearch(int needle) {
+		if ((needle & 0xff000000) != 0) {
+			throw new IllegalArgumentException("Too big value for 3 bytes"); //$NON-NLS-1$
+		}
+		if (size == 0) {
+			return -1;
+		}
+		int high = size;
+		if (high == 0)
+			return -1;
+		int low = 0;
+		do {
+			int mid = (low + high) >>> 1;
+			int cmp;
+			cmp = Integer.compare(needle, get(mid));
+			if (cmp < 0)
+				high = mid;
+			else if (cmp == 0) {
+				return mid;
+			} else
+				low = mid + 1;
+		} while (low < high);
+		return -1;
+	}
+
+	int getLastValue() {
+		return get(size - 1);
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCursor.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCursor.java
index e7fd7b9..fa743ba 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCursor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCursor.java
@@ -16,6 +16,7 @@
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Optional;
 import java.util.Set;
 import java.util.zip.DataFormatException;
 import java.util.zip.Inflater;
@@ -34,6 +35,7 @@
 import org.eclipse.jgit.lib.AnyObjectId;
 import org.eclipse.jgit.lib.BitmapIndex;
 import org.eclipse.jgit.lib.BitmapIndex.BitmapBuilder;
+import org.eclipse.jgit.internal.storage.commitgraph.CommitGraph;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.InflaterCache;
 import org.eclipse.jgit.lib.ObjectId;
@@ -96,6 +98,12 @@
 
 	/** {@inheritDoc} */
 	@Override
+	public Optional<CommitGraph> getCommitGraph() {
+		return db.getCommitGraph();
+	}
+
+	/** {@inheritDoc} */
+	@Override
 	public Collection<CachedPack> getCachedPacksAndUpdate(
 			BitmapBuilder needBitmap) throws IOException {
 		for (Pack pack : db.getPacks()) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/memory/TernarySearchTree.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/memory/TernarySearchTree.java
new file mode 100644
index 0000000..1ac6627
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/memory/TernarySearchTree.java
@@ -0,0 +1,597 @@
+/*
+ * Copyright (C) 2021, Matthias Sohn <matthias.sohn@sap.com> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.storage.memory;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Queue;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.util.StringUtils;
+
+/**
+ * A ternary search tree with String keys and generic values.
+ *
+ * TernarySearchTree is a type of trie (sometimes called a prefix tree) where
+ * nodes are arranged in a manner similar to a binary search tree, but with up
+ * to three children rather than the binary tree's limit of two. Like other
+ * prefix trees, a ternary search tree can be used as an associative map
+ * structure with the ability for incremental string search. However, ternary
+ * search trees are more space efficient compared to standard prefix trees, at
+ * the cost of speed.
+ *
+ * Keys must not be null or empty. Values cannot be null.
+ *
+ * This class is thread safe.
+ *
+ * @param <Value>
+ *            type of values in this tree
+ * @since 6.5
+ */
+public final class TernarySearchTree<Value> {
+
+	private static final char WILDCARD = '?';
+
+	private static class Node<Value> {
+		final char c;
+
+		Node<Value> lo, eq, hi;
+
+		Value val;
+
+		Node(char c) {
+			this.c = c;
+		}
+
+		boolean hasValue() {
+			return val != null;
+		}
+	}
+
+	/**
+	 * Loader to load key-value pairs to be cached in the tree
+	 *
+	 * @param <Value>
+	 *            type of values
+	 */
+	public static interface Loader<Value> {
+		/**
+		 * Load map of all key value pairs
+		 *
+		 * @return map of all key value pairs to cache in the tree
+		 */
+		Map<String, Value> loadAll();
+	}
+
+	private static void validateKey(String key) {
+		if (StringUtils.isEmptyOrNull(key)) {
+			throw new IllegalArgumentException(
+					JGitText.get().illegalTernarySearchTreeKey);
+		}
+	}
+
+	private static <V> void validateValue(V value) {
+		if (value == null) {
+			throw new IllegalArgumentException(
+					JGitText.get().illegalTernarySearchTreeValue);
+		}
+	}
+
+	private final ReadWriteLock lock;
+
+	private final AtomicInteger size = new AtomicInteger(0);
+
+	private Node<Value> root;
+
+	/**
+	 * Construct a new ternary search tree
+	 */
+	public TernarySearchTree() {
+		lock = new ReentrantReadWriteLock();
+	}
+
+	/**
+	 * Get the lock guarding read and write access to the cache.
+	 *
+	 * @return lock guarding read and write access to the cache
+	 */
+	public ReadWriteLock getLock() {
+		return lock;
+	}
+
+	/**
+	 * Replace the tree with a new tree containing all entries provided by an
+	 * iterable
+	 *
+	 * @param loader
+	 *            iterable providing key-value pairs to load
+	 * @return number of key-value pairs after replacing finished
+	 */
+	public int replace(Iterable<Entry<String, Value>> loader) {
+		lock.writeLock().lock();
+		try {
+			clear();
+			for (Entry<String, Value> e : loader) {
+				insertImpl(e.getKey(), e.getValue());
+			}
+		} finally {
+			lock.writeLock().unlock();
+		}
+		return size.get();
+	}
+
+	/**
+	 * Reload the tree entries provided by loader
+	 *
+	 * @param loader
+	 *            iterable providing key-value pairs to load
+	 * @return number of key-value pairs
+	 */
+	public int reload(Iterable<Entry<String, Value>> loader) {
+		lock.writeLock().lock();
+		try {
+			for (Entry<String, Value> e : loader) {
+				insertImpl(e.getKey(), e.getValue());
+			}
+		} finally {
+			lock.writeLock().unlock();
+		}
+		return size.get();
+	}
+
+	/**
+	 * Delete entries
+	 *
+	 * @param delete
+	 *            iterable providing keys of entries to be deleted
+	 * @return number of key-value pairs
+	 */
+	public int delete(Iterable<String> delete) {
+		lock.writeLock().lock();
+		try {
+			for (String s : delete) {
+				delete(s);
+			}
+		} finally {
+			lock.writeLock().unlock();
+		}
+		return size.get();
+	}
+
+	/**
+	 * Get the number of key value pairs in this trie
+	 *
+	 * @return number of key value pairs in this trie
+	 */
+	public int size() {
+		return size.get();
+	}
+
+	/**
+	 * Get the value associated to a key or {@code null}.
+	 *
+	 * @param key
+	 *            the key
+	 * @return the value associated to this key
+	 */
+	@Nullable
+	public Value get(String key) {
+		validateKey(key);
+		lock.readLock().lock();
+		try {
+			Node<Value> node = get(root, key, 0);
+			if (node == null) {
+				return null;
+			}
+			return node.val;
+		} finally {
+			lock.readLock().unlock();
+		}
+	}
+
+	/**
+	 * Check whether this tree contains the given key.
+	 *
+	 * @param key
+	 *            a key
+	 * @return whether this tree contains this key
+	 */
+	public boolean contains(String key) {
+		return get(key) != null;
+	}
+
+	/**
+	 * Insert a key-value pair. If the key already exists the old value is
+	 * overwritten.
+	 *
+	 * @param key
+	 *            the key
+	 * @param value
+	 *            the value
+	 * @return number of key-value pairs after adding the entry
+	 */
+	public int insert(String key, Value value) {
+		lock.writeLock().lock();
+		try {
+			insertImpl(key, value);
+			return size.get();
+		} finally {
+			lock.writeLock().unlock();
+		}
+	}
+
+	/**
+	 * Insert map of key-value pairs. Values of existing keys are overwritten.
+	 * Use this method to insert multiple key-value pairs.
+	 *
+	 * @param map
+	 *            map of key-value pairs to insert
+	 * @return number of key-value pairs after adding entries
+	 */
+	public int insert(Map<String, Value> map) {
+		lock.writeLock().lock();
+		try {
+			for (Entry<String, Value> e : map.entrySet()) {
+				insertImpl(e.getKey(), e.getValue());
+			}
+			return size.get();
+		} finally {
+			lock.writeLock().unlock();
+		}
+	}
+
+	private void insertImpl(String key, Value value) {
+		validateValue(value);
+		if (!contains(key)) {
+			size.addAndGet(1);
+		}
+		root = insert(root, key, value, 0);
+	}
+
+	/**
+	 * Delete a key-value pair. Does nothing if the key doesn't exist.
+	 *
+	 * @param key
+	 *            the key
+	 * @return number of key-value pairs after the deletion
+	 */
+	public int delete(String key) {
+		lock.writeLock().lock();
+		try {
+			if (contains(key)) {
+				size.addAndGet(-1);
+				root = insert(root, key, null, 0);
+			}
+			return size.get();
+		} finally {
+			lock.writeLock().unlock();
+		}
+	}
+
+	/**
+	 * Remove all key value pairs from this tree
+	 */
+	public void clear() {
+		lock.writeLock().lock();
+		try {
+			size.set(0);
+			root = null;
+		} finally {
+			lock.writeLock().unlock();
+		}
+	}
+
+	/**
+	 * Find the key which is the longest prefix of the given query string.
+	 *
+	 * @param query
+	 * @return the key which is the longest prefix of the given query string or
+	 *         {@code null} if none exists.
+	 */
+	@Nullable
+	public String keyLongestPrefixOf(String query) {
+		if (StringUtils.isEmptyOrNull(query)) {
+			return null;
+		}
+		lock.readLock().lock();
+		try {
+			int length = 0;
+			Node<Value> node = root;
+			int i = 0;
+			while (node != null && i < query.length()) {
+				char c = query.charAt(i);
+				if (node.c > c) {
+					node = node.lo;
+				} else if (node.c < c) {
+					node = node.hi;
+				} else {
+					i++;
+					if (node.hasValue()) {
+						length = i;
+					}
+					node = node.eq;
+				}
+			}
+			return query.substring(0, length);
+		} finally {
+			lock.readLock().unlock();
+		}
+	}
+
+	/**
+	 * Get all keys.
+	 *
+	 * @return all keys
+	 */
+	public Iterable<String> getKeys() {
+		Queue<String> queue = new LinkedList<>();
+		lock.readLock().lock();
+		try {
+			findKeysWithPrefix(root, new StringBuilder(), queue);
+			return queue;
+		} finally {
+			lock.readLock().unlock();
+		}
+	}
+
+	/**
+	 * Get keys starting with given prefix
+	 *
+	 * @param prefix
+	 *            key prefix
+	 * @return keys starting with given prefix
+	 */
+	public Iterable<String> getKeysWithPrefix(String prefix) {
+		Queue<String> keys = new LinkedList<>();
+		if (prefix == null) {
+			return keys;
+		}
+		if (prefix.isEmpty()) {
+			return getKeys();
+		}
+		lock.readLock().lock();
+		try {
+			validateKey(prefix);
+			Node<Value> node = get(root, prefix, 0);
+			if (node == null) {
+				return keys;
+			}
+			if (node.hasValue()) {
+				keys.add(prefix);
+			}
+			findKeysWithPrefix(node.eq, new StringBuilder(prefix), keys);
+			return keys;
+		} finally {
+			lock.readLock().unlock();
+		}
+	}
+
+	/**
+	 * Get all entries.
+	 *
+	 * @return all entries
+	 */
+	public Map<String, Value> getAll() {
+		Map<String, Value> entries = new HashMap<>();
+		lock.readLock().lock();
+		try {
+			findWithPrefix(root, new StringBuilder(), entries);
+			return entries;
+		} finally {
+			lock.readLock().unlock();
+		}
+	}
+
+	/**
+	 * Get all values.
+	 *
+	 * @return all values
+	 */
+	public List<Value> getAllValues() {
+		List<Value> values = new ArrayList<>();
+		lock.readLock().lock();
+		try {
+			findValuesWithPrefix(root, new StringBuilder(), values);
+			return values;
+		} finally {
+			lock.readLock().unlock();
+		}
+	}
+
+	/**
+	 * Get all entries with given prefix
+	 *
+	 * @param prefix
+	 *            key prefix
+	 * @return entries with given prefix
+	 */
+	public Map<String, Value> getWithPrefix(String prefix) {
+		Map<String, Value> entries = new HashMap<>();
+		if (prefix == null) {
+			return entries;
+		}
+		if (prefix.isEmpty()) {
+			return getAll();
+		}
+		lock.readLock().lock();
+		try {
+			validateKey(prefix);
+			Node<Value> node = get(root, prefix, 0);
+			if (node == null) {
+				return entries;
+			}
+			if (node.hasValue()) {
+				entries.put(prefix, node.val);
+			}
+			findWithPrefix(node.eq, new StringBuilder(prefix), entries);
+			return entries;
+		} finally {
+			lock.readLock().unlock();
+		}
+	}
+
+	/**
+	 * Get all values with given key prefix
+	 *
+	 * @param prefix
+	 *            key prefix
+	 * @return entries with given prefix
+	 */
+	public List<Value> getValuesWithPrefix(String prefix) {
+		List<Value> values = new ArrayList<>();
+		if (prefix == null) {
+			return values;
+		}
+		if (prefix.isEmpty()) {
+			return getAllValues();
+		}
+		lock.readLock().lock();
+		try {
+			validateKey(prefix);
+			Node<Value> node = get(root, prefix, 0);
+			if (node == null) {
+				return values;
+			}
+			if (node.hasValue()) {
+				values.add(node.val);
+			}
+			findValuesWithPrefix(node.eq, new StringBuilder(prefix), values);
+			return values;
+		} finally {
+			lock.readLock().unlock();
+		}
+	}
+
+	/**
+	 * Get keys matching given pattern using '?' as wildcard character.
+	 *
+	 * @param pattern
+	 *            search pattern
+	 * @return keys matching given pattern.
+	 */
+	public Iterable<String> getKeysMatching(String pattern) {
+		Queue<String> keys = new LinkedList<>();
+		lock.readLock().lock();
+		try {
+			findKeysWithPrefix(root, new StringBuilder(), 0, pattern, keys);
+			return keys;
+		} finally {
+			lock.readLock().unlock();
+		}
+	}
+
+	private Node<Value> get(Node<Value> node, String key, int depth) {
+		if (node == null) {
+			return null;
+		}
+		char c = key.charAt(depth);
+		if (node.c > c) {
+			return get(node.lo, key, depth);
+		} else if (node.c < c) {
+			return get(node.hi, key, depth);
+		} else if (depth < key.length() - 1) {
+			return get(node.eq, key, depth + 1);
+		} else {
+			return node;
+		}
+	}
+
+	private Node<Value> insert(Node<Value> node, String key, Value val,
+			int depth) {
+		char c = key.charAt(depth);
+		if (node == null) {
+			node = new Node<>(c);
+		}
+		if (node.c > c) {
+			node.lo = insert(node.lo, key, val, depth);
+		} else if (node.c < c) {
+			node.hi = insert(node.hi, key, val, depth);
+		} else if (depth < key.length() - 1) {
+			node.eq = insert(node.eq, key, val, depth + 1);
+		} else {
+			node.val = val;
+		}
+		return node;
+	}
+
+	private void findKeysWithPrefix(Node<Value> node, StringBuilder prefix,
+			Queue<String> keys) {
+		if (node == null) {
+			return;
+		}
+		findKeysWithPrefix(node.lo, prefix, keys);
+		if (node.hasValue()) {
+			keys.add(prefix.toString() + node.c);
+		}
+		findKeysWithPrefix(node.eq, prefix.append(node.c), keys);
+		prefix.deleteCharAt(prefix.length() - 1);
+		findKeysWithPrefix(node.hi, prefix, keys);
+	}
+
+	private void findWithPrefix(Node<Value> node, StringBuilder prefix,
+			Map<String, Value> entries) {
+		if (node == null) {
+			return;
+		}
+		findWithPrefix(node.lo, prefix, entries);
+		if (node.hasValue()) {
+			entries.put(prefix.toString() + node.c, node.val);
+		}
+		findWithPrefix(node.eq, prefix.append(node.c), entries);
+		prefix.deleteCharAt(prefix.length() - 1);
+		findWithPrefix(node.hi, prefix, entries);
+	}
+
+	private void findValuesWithPrefix(Node<Value> node, StringBuilder prefix,
+			List<Value> values) {
+		if (node == null) {
+			return;
+		}
+		findValuesWithPrefix(node.lo, prefix, values);
+		if (node.hasValue()) {
+			values.add(node.val);
+		}
+		findValuesWithPrefix(node.eq, prefix.append(node.c), values);
+		prefix.deleteCharAt(prefix.length() - 1);
+		findValuesWithPrefix(node.hi, prefix, values);
+	}
+
+	private void findKeysWithPrefix(Node<Value> node, StringBuilder prefix,
+			int i, String pattern, Queue<String> keys) {
+		if (node == null || StringUtils.isEmptyOrNull(pattern)) {
+			return;
+		}
+		char c = pattern.charAt(i);
+		if (c == WILDCARD || node.c > c) {
+			findKeysWithPrefix(node.lo, prefix, i, pattern, keys);
+		}
+		if (c == WILDCARD || node.c == c) {
+			if (i == pattern.length() - 1 && node.hasValue()) {
+				keys.add(prefix.toString() + node.c);
+			}
+			if (i < pattern.length() - 1) {
+				findKeysWithPrefix(node.eq, prefix.append(node.c), i + 1,
+						pattern, keys);
+				prefix.deleteCharAt(prefix.length() - 1);
+			}
+		}
+		if (c == WILDCARD || node.c < c) {
+			findKeysWithPrefix(node.hi, prefix, i, pattern, keys);
+		}
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackExt.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackExt.java
index c006995..adad411 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackExt.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackExt.java
@@ -30,7 +30,13 @@
 	REFTABLE("ref"), //$NON-NLS-1$
 
 	/** A pack reverse index file extension. */
-	REVERSE_INDEX("rev"); //$NON-NLS-1$
+	REVERSE_INDEX("rev"), //$NON-NLS-1$
+
+	/** A commit graph file extension. */
+	COMMIT_GRAPH("graph"), //$NON-NLS-1$
+
+	/** An object size index. */
+	OBJECT_SIZE_INDEX("objsize"); //$NON-NLS-1$
 
 	private final String ext;
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java
index d43d8bb..60edc76 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java
@@ -61,6 +61,7 @@
 import org.eclipse.jgit.internal.storage.file.PackBitmapIndexBuilder;
 import org.eclipse.jgit.internal.storage.file.PackBitmapIndexWriterV1;
 import org.eclipse.jgit.internal.storage.file.PackIndexWriter;
+import org.eclipse.jgit.internal.storage.file.PackObjectSizeIndexWriter;
 import org.eclipse.jgit.lib.AnyObjectId;
 import org.eclipse.jgit.lib.AsyncObjectSizeQueue;
 import org.eclipse.jgit.lib.BatchingProgressMonitor;
@@ -1114,6 +1115,50 @@
 	}
 
 	/**
+	 * Create an object size index file for the contents of the pack file just
+	 * written.
+	 * <p>
+	 * Called after
+	 * {@link #writePack(ProgressMonitor, ProgressMonitor, OutputStream)} that
+	 * populates the list of objects to pack and before
+	 * {@link #writeBitmapIndex(OutputStream)} that destroys it.
+	 * <p>
+	 * Writing this index is only required for local pack storage. Packs sent on
+	 * the network do not need to create an object size index.
+	 *
+	 * @param objIdxStream
+	 *            output for the object size index data. Caller is responsible
+	 *            for closing this stream.
+	 * @throws IOException
+	 *             errors while writing
+	 */
+	public void writeObjectSizeIndex(OutputStream objIdxStream)
+			throws IOException {
+		if (config.getMinBytesForObjSizeIndex() < 0) {
+			return;
+		}
+
+		long writeStart = System.currentTimeMillis();
+		// We only need to populate the size of blobs
+		AsyncObjectSizeQueue<ObjectToPack> sizeQueue = reader
+				.getObjectSize(objectsLists[OBJ_BLOB], /* reportMissing= */false);
+		try {
+			while (sizeQueue.next()) {
+				ObjectToPack otp = sizeQueue.getCurrent();
+				long sz = sizeQueue.getSize();
+				otp.setFullSize(sz);
+			}
+		} finally {
+			sizeQueue.release();
+		}
+		PackObjectSizeIndexWriter iw = PackObjectSizeIndexWriter.createWriter(
+				objIdxStream, config.getMinBytesForObjSizeIndex());
+		// All indexed objects because their positions must match primary index order
+		iw.write(sortByName());
+		stats.timeWriting += System.currentTimeMillis() - writeStart;
+	}
+
+	/**
 	 * Create a bitmap index file to match the pack file just written.
 	 * <p>
 	 * Called after {@link #prepareBitmapIndex(ProgressMonitor)}.
@@ -2393,10 +2438,14 @@
 
 		int numCommits = objectsLists[OBJ_COMMIT].size();
 		List<ObjectToPack> byName = sortByName();
+		// Reset sortedByName before the array that it points to is mutated by
+		// PackBitmapIndexBuilder, to prevent other methods referencing the
+		// mutated array afterwards.
 		sortedByName = null;
 		objectsLists = null;
 		objectsMap = null;
 		writeBitmaps = new PackBitmapIndexBuilder(byName);
+		// Allow byName to be GC'd if JVM GC runs before the end of the method.
 		byName = null;
 
 		PackWriterBitmapPreparer bitmapPreparer = new PackWriterBitmapPreparer(
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BatchRefUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BatchRefUpdate.java
index ef1379a..e2bebfe 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BatchRefUpdate.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BatchRefUpdate.java
@@ -498,17 +498,14 @@
 				try {
 					if (cmd.getResult() == NOT_ATTEMPTED) {
 						cmd.updateType(walk);
-						RefUpdate ru = newUpdate(cmd);
 						switch (cmd.getType()) {
 							case DELETE:
 								// Performed in the first phase
 								break;
 							case UPDATE:
 							case UPDATE_NONFASTFORWARD:
-								RefUpdate ruu = newUpdate(cmd);
-								cmd.setResult(ruu.update(walk));
-								break;
 							case CREATE:
+								RefUpdate ru = newUpdate(cmd);
 								cmd.setResult(ru.update(walk));
 								break;
 						}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BatchingProgressMonitor.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BatchingProgressMonitor.java
index 49e295a..f826057 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BatchingProgressMonitor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BatchingProgressMonitor.java
@@ -10,21 +10,29 @@
 
 package org.eclipse.jgit.lib;
 
+import java.time.Duration;
+import java.time.Instant;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 
 import org.eclipse.jgit.lib.internal.WorkQueue;
+import org.eclipse.jgit.util.SystemReader;
 
 /**
  * ProgressMonitor that batches update events.
  */
 public abstract class BatchingProgressMonitor implements ProgressMonitor {
+	private static boolean performanceTrace = SystemReader.getInstance()
+			.isPerformanceTraceEnabled();
+
 	private long delayStartTime;
 
 	private TimeUnit delayStartUnit = TimeUnit.MILLISECONDS;
 
 	private Task task;
 
+	private Boolean showDuration;
+
 	/**
 	 * Set an optional delay before the first output.
 	 *
@@ -76,6 +84,11 @@
 		return false;
 	}
 
+	@Override
+	public void showDuration(boolean enabled) {
+		showDuration = Boolean.valueOf(enabled);
+	}
+
 	/**
 	 * Update the progress monitor if the total work isn't known,
 	 *
@@ -83,8 +96,12 @@
 	 *            name of the task.
 	 * @param workCurr
 	 *            number of units already completed.
+	 * @param duration
+	 *            how long this task runs
+	 * @since 6.5
 	 */
-	protected abstract void onUpdate(String taskName, int workCurr);
+	protected abstract void onUpdate(String taskName, int workCurr,
+			Duration duration);
 
 	/**
 	 * Finish the progress monitor when the total wasn't known in advance.
@@ -93,8 +110,12 @@
 	 *            name of the task.
 	 * @param workCurr
 	 *            total number of units processed.
+	 * @param duration
+	 *            how long this task runs
+	 * @since 6.5
 	 */
-	protected abstract void onEndTask(String taskName, int workCurr);
+	protected abstract void onEndTask(String taskName, int workCurr,
+			Duration duration);
 
 	/**
 	 * Update the progress monitor when the total is known in advance.
@@ -107,9 +128,12 @@
 	 *            estimated number of units to process.
 	 * @param percentDone
 	 *            {@code workCurr * 100 / workTotal}.
+	 * @param duration
+	 *            how long this task runs
+	 * @since 6.5
 	 */
 	protected abstract void onUpdate(String taskName, int workCurr,
-			int workTotal, int percentDone);
+			int workTotal, int percentDone, Duration duration);
 
 	/**
 	 * Finish the progress monitor when the total is known in advance.
@@ -122,9 +146,58 @@
 	 *            estimated number of units to process.
 	 * @param percentDone
 	 *            {@code workCurr * 100 / workTotal}.
+	 * @param duration
+	 *            duration of the task
+	 * @since 6.5
 	 */
 	protected abstract void onEndTask(String taskName, int workCurr,
-			int workTotal, int percentDone);
+			int workTotal, int percentDone, Duration duration);
+
+	private boolean showDuration() {
+		return showDuration != null ? showDuration.booleanValue()
+				: performanceTrace;
+	}
+
+	/**
+	 * Append formatted duration if system property or environment variable
+	 * GIT_TRACE_PERFORMANCE is set to "true". If both are defined the system
+	 * property takes precedence.
+	 *
+	 * @param s
+	 *            StringBuilder to append the formatted duration to
+	 * @param duration
+	 *            duration to format
+	 * @since 6.5
+	 */
+	@SuppressWarnings({ "boxing", "nls" })
+	protected void appendDuration(StringBuilder s, Duration duration) {
+		if (!showDuration()) {
+			return;
+		}
+		long hours = duration.toHours();
+		int minutes = duration.toMinutesPart();
+		int seconds = duration.toSecondsPart();
+		s.append(" [");
+		if (hours > 0) {
+			s.append(hours).append(':');
+			s.append(String.format("%02d", minutes)).append(':');
+			s.append(String.format("%02d", seconds));
+		} else if (minutes > 0) {
+			s.append(minutes).append(':');
+			s.append(String.format("%02d", seconds));
+		} else {
+			s.append(seconds);
+		}
+		s.append('.').append(String.format("%03d", duration.toMillisPart()));
+		if (hours > 0) {
+			s.append('h');
+		} else if (minutes > 0) {
+			s.append('m');
+		} else {
+			s.append('s');
+		}
+		s.append(']');
+	}
 
 	private static class Task implements Runnable {
 		/** Title of the current task. */
@@ -148,10 +221,13 @@
 		/** Percentage of {@link #totalWork} that is done. */
 		private int lastPercent;
 
+		private final Instant startTime;
+
 		Task(String taskName, int totalWork) {
 			this.taskName = taskName;
 			this.totalWork = totalWork;
 			this.display = true;
+			this.startTime = Instant.now();
 		}
 
 		void delay(long time, TimeUnit unit) {
@@ -170,7 +246,7 @@
 			if (totalWork == UNKNOWN) {
 				// Only display once per second, as the alarm fires.
 				if (display) {
-					pm.onUpdate(taskName, lastWork);
+					pm.onUpdate(taskName, lastWork, elapsedTime());
 					output = true;
 					restartTimer();
 				}
@@ -178,12 +254,14 @@
 				// Display once per second or when 1% is done.
 				int currPercent = Math.round(lastWork * 100F / totalWork);
 				if (display) {
-					pm.onUpdate(taskName, lastWork, totalWork, currPercent);
+					pm.onUpdate(taskName, lastWork, totalWork, currPercent,
+							elapsedTime());
 					output = true;
 					restartTimer();
 					lastPercent = currPercent;
 				} else if (currPercent != lastPercent) {
-					pm.onUpdate(taskName, lastWork, totalWork, currPercent);
+					pm.onUpdate(taskName, lastWork, totalWork, currPercent,
+							elapsedTime());
 					output = true;
 					lastPercent = currPercent;
 				}
@@ -199,14 +277,18 @@
 		void end(BatchingProgressMonitor pm) {
 			if (output) {
 				if (totalWork == UNKNOWN) {
-					pm.onEndTask(taskName, lastWork);
+					pm.onEndTask(taskName, lastWork, elapsedTime());
 				} else {
 					int currPercent = Math.round(lastWork * 100F / totalWork);
-					pm.onEndTask(taskName, lastWork, totalWork, currPercent);
+					pm.onEndTask(taskName, lastWork, totalWork, currPercent, elapsedTime());
 				}
 			}
 			if (timerFuture != null)
 				timerFuture.cancel(false /* no interrupt */);
 		}
+
+		private Duration elapsedTime() {
+			return Duration.between(startTime, Instant.now());
+		}
 	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BranchConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BranchConfig.java
index aa613d0..e15c7af 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BranchConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BranchConfig.java
@@ -30,8 +30,17 @@
 
 		/** Value for rebasing */
 		REBASE("true"), //$NON-NLS-1$
-		/** Value for rebasing preserving local merge commits */
-		PRESERVE("preserve"), //$NON-NLS-1$
+		/**
+		 * Value for rebasing preserving local merge commits
+		 *
+		 * @since 6.5 used instead of deprecated "preserve" option
+		 */
+		MERGES("merges"){ //$NON-NLS-1$
+		    @Override
+		    public boolean matchConfigValue(String s) {
+		      return super.matchConfigValue(s) || "preserve".equals(s); //$NON-NLS-1$
+		    }
+		},
 		/** Value for rebasing interactively */
 		INTERACTIVE("interactive"), //$NON-NLS-1$
 		/** Value for not rebasing at all but merging */
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
index 2b49f95..7d6f40a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
@@ -835,6 +835,13 @@
 	public static final String CONFIG_KEY_WINDOW_MEMORY = "windowmemory";
 
 	/**
+	 * the "pack.minBytesForObjSizeIndex" key
+	 *
+	 * @since 6.5
+	 */
+	public static final String CONFIG_KEY_MIN_BYTES_OBJ_SIZE_INDEX = "minBytesForObjSizeIndex";
+
+	/**
 	 * The "repack.packKeptObjects" key
 	 *
 	 * @since 5.13.3
@@ -912,6 +919,20 @@
 	public static final String CONFIG_KEY_ABBREV = "abbrev";
 
 	/**
+	 * The "writeCommitGraph" key
+	 *
+	 * @since 6.5
+	 */
+	public static final String CONFIG_KEY_WRITE_COMMIT_GRAPH = "writeCommitGraph";
+
+	/**
+	 * The "commitGraph" used by commit-graph feature
+	 *
+	 * @since 6.5
+	 */
+	public static final String CONFIG_COMMIT_GRAPH = "commitGraph";
+
+	/**
 	 * The "trustPackedRefsStat" key
 	 *
 	 * @since 6.1.1
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 30a0074..0b8bf8c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
@@ -284,6 +284,12 @@
 	 */
 	public static final String INFO_HTTP_ALTERNATES = "info/http-alternates";
 
+	/**
+	 * info commit-graph file (goes under OBJECTS)
+	 * @since 6.5
+	 */
+	public static final String INFO_COMMIT_GRAPH = "info/commit-graph";
+
 	/** Packed refs file */
 	public static final String PACKED_REFS = "packed-refs";
 
@@ -754,6 +760,23 @@
 	 */
 	public static final int INFINITE_DEPTH = 0x7fffffff;
 
+	/**
+	 * We use ({@value}) as generation number for commits not in the
+	 * commit-graph file.
+	 *
+	 * @since 6.5
+	 */
+	public static int COMMIT_GENERATION_UNKNOWN = Integer.MAX_VALUE;
+
+	/**
+	 * If a commit-graph file was written by a version of Git that did not
+	 * compute generation numbers, then those commits will have generation
+	 * number represented by ({@value}).
+	 *
+	 * @since 6.5
+	 */
+	public static int COMMIT_GENERATION_NOT_COMPUTED = 0;
+
 	private Constants() {
 		// Hide the default constructor
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java
index fc82a5f..4de1801 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java
@@ -117,6 +117,13 @@
 	}
 
 	/**
+	 * Default value of commit graph enable option: {@value}
+	 *
+	 * @since 6.5
+	 */
+	public static final boolean DEFAULT_COMMIT_GRAPH_ENABLE = false;
+
+	/**
 	 * Permissible values for {@code core.trustPackedRefsStat}.
 	 *
 	 * @since 6.1.1
@@ -147,6 +154,8 @@
 
 	private final String attributesfile;
 
+	private final boolean commitGraph;
+
 	/**
 	 * Options for symlink handling
 	 *
@@ -188,6 +197,9 @@
 				ConfigConstants.CONFIG_KEY_EXCLUDESFILE);
 		attributesfile = rc.getString(ConfigConstants.CONFIG_CORE_SECTION,
 				null, ConfigConstants.CONFIG_KEY_ATTRIBUTESFILE);
+		commitGraph = rc.getBoolean(ConfigConstants.CONFIG_CORE_SECTION,
+				ConfigConstants.CONFIG_COMMIT_GRAPH,
+				DEFAULT_COMMIT_GRAPH_ENABLE);
 	}
 
 	/**
@@ -240,4 +252,16 @@
 	public String getAttributesFile() {
 		return attributesfile;
 	}
+
+	/**
+	 * Whether to read the commit-graph file (if it exists) to parse the graph
+	 * structure of commits. Default to
+	 * {@value org.eclipse.jgit.lib.CoreConfig#DEFAULT_COMMIT_GRAPH_ENABLE}.
+	 *
+	 * @return whether to read the commit-graph file
+	 * @since 6.5
+	 */
+	public boolean enableCommitGraph() {
+		return commitGraph;
+	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/EmptyProgressMonitor.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/EmptyProgressMonitor.java
index 6b201e6..94d28eb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/EmptyProgressMonitor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/EmptyProgressMonitor.java
@@ -48,4 +48,8 @@
 		return false;
 	}
 
+	@Override
+	public void showDuration(boolean enabled) {
+		// not implemented
+	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/NullProgressMonitor.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/NullProgressMonitor.java
index 10904b6..127cca9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/NullProgressMonitor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/NullProgressMonitor.java
@@ -52,4 +52,9 @@
 	public void endTask() {
 		// Do not report.
 	}
+
+	@Override
+	public void showDuration(boolean enabled) {
+		// don't show
+	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java
index 081f40e..69b2b51 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java
@@ -17,6 +17,7 @@
 import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Optional;
 import java.util.Set;
 
 import org.eclipse.jgit.annotations.NonNull;
@@ -27,6 +28,7 @@
 import org.eclipse.jgit.internal.revwalk.BitmappedReachabilityChecker;
 import org.eclipse.jgit.internal.revwalk.PedestrianObjectReachabilityChecker;
 import org.eclipse.jgit.internal.revwalk.PedestrianReachabilityChecker;
+import org.eclipse.jgit.internal.storage.commitgraph.CommitGraph;
 import org.eclipse.jgit.revwalk.ObjectReachabilityChecker;
 import org.eclipse.jgit.revwalk.ObjectWalk;
 import org.eclipse.jgit.revwalk.ReachabilityChecker;
@@ -500,6 +502,26 @@
 	}
 
 	/**
+	 * Get the commit-graph for this repository if available.
+	 * <p>
+	 * The commit graph can be created/modified/deleted while the repository is
+	 * open and specific implementations decide when to refresh it.
+	 *
+	 * @return the commit-graph or empty if the commit-graph does not exist or
+	 *         is invalid; always returns empty when core.commitGraph is false
+	 *         (default is
+	 *         {@value org.eclipse.jgit.lib.CoreConfig#DEFAULT_COMMIT_GRAPH_ENABLE}).
+	 *
+	 * @throws IOException
+	 *             if it cannot open any of the underlying commit graph.
+	 *
+	 * @since 6.5
+	 */
+	public Optional<CommitGraph> getCommitGraph() throws IOException {
+		return Optional.empty();
+	}
+
+	/**
 	 * Get the {@link org.eclipse.jgit.lib.ObjectInserter} from which this
 	 * reader was created using {@code inserter.newReader()}
 	 *
@@ -642,6 +664,11 @@
 		}
 
 		@Override
+		public Optional<CommitGraph> getCommitGraph() throws IOException{
+			return delegate().getCommitGraph();
+		}
+
+		@Override
 		@Nullable
 		public ObjectInserter getCreatedFromInserter() {
 			return delegate().getCreatedFromInserter();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ProgressMonitor.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ProgressMonitor.java
index 9ebb0a4..2ce73ac 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ProgressMonitor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ProgressMonitor.java
@@ -65,4 +65,13 @@
 	 * @return true if the user asked the process to stop working.
 	 */
 	boolean isCancelled();
+
+	/**
+	 * Set whether the monitor should show elapsed time per task
+	 *
+	 * @param enabled
+	 *            whether to show elapsed time per task
+	 * @since 6.5
+	 */
+	void showDuration(boolean enabled);
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java
index 7b7bdeb..98089fb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java
@@ -40,10 +40,10 @@
 	/**
 	 * Order of prefixes to search when using non-absolute references.
 	 * <p>
-	 * {@link #findRef(String)} takes this search space into consideration
-	 * when locating a reference by name. The first entry in the path is
-	 * always {@code ""}, ensuring that absolute references are resolved
-	 * without further mangling.
+	 * {@link #findRef(String)} takes this search space into consideration when
+	 * locating a reference by name. The first entry in the path is always
+	 * {@code ""}, ensuring that absolute references are resolved without
+	 * further mangling.
 	 */
 	protected static final String[] SEARCH_PATH = { "", //$NON-NLS-1$
 			Constants.R_REFS, //
@@ -69,6 +69,15 @@
 	public static final String ALL = "";//$NON-NLS-1$
 
 	/**
+	 * The names of additional refs
+	 *
+	 * @since 6.5
+	 */
+	protected static final String[] additionalRefsNames = new String[] {
+			Constants.MERGE_HEAD, Constants.FETCH_HEAD, Constants.ORIG_HEAD,
+			Constants.CHERRY_PICK_HEAD, Constants.REVERT_HEAD };
+
+	/**
 	 * Initialize a new reference database at this location.
 	 *
 	 * @throws java.io.IOException
@@ -341,12 +350,12 @@
 	/**
 	 * Returns all refs.
 	 * <p>
-	 * This includes {@code HEAD}, branches under {@code ref/heads/}, tags
-	 * under {@code refs/tags/}, etc. It does not include pseudo-refs like
+	 * This includes {@code HEAD}, branches under {@code ref/heads/}, tags under
+	 * {@code refs/tags/}, etc. It does not include pseudo-refs like
 	 * {@code FETCH_HEAD}; for those, see {@link #getAdditionalRefs}.
 	 * <p>
-	 * Symbolic references to a non-existent ref (for example,
-	 * {@code HEAD} pointing to a branch yet to be born) are not included.
+	 * Symbolic references to a non-existent ref (for example, {@code HEAD}
+	 * pointing to a branch yet to be born) are not included.
 	 * <p>
 	 * Callers interested in only a portion of the ref hierarchy can call
 	 * {@link #getRefsByPrefix} instead.
@@ -386,8 +395,9 @@
 	 * {@link RefDatabase} should override this method directly if a better
 	 * implementation is possible.
 	 *
-	 * @param prefix string that names of refs should start with; may be
-	 *             empty (to return all refs).
+	 * @param prefix
+	 *            string that names of refs should start with; may be empty (to
+	 *            return all refs).
 	 * @return immutable list of refs whose names start with {@code prefix}.
 	 * @throws java.io.IOException
 	 *             the reference space cannot be accessed.
@@ -417,18 +427,22 @@
 	}
 
 	/**
-	 * Returns refs whose names start with a given prefix excluding all refs that
-	 * start with one of the given prefixes.
+	 * Returns refs whose names start with a given prefix excluding all refs
+	 * that start with one of the given prefixes.
 	 *
 	 * <p>
-	 * The default implementation is not efficient. Implementors of {@link RefDatabase}
-	 * should override this method directly if a better implementation is possible.
-	 * 
-	 * @param include string that names of refs should start with; may be empty.
-	 * @param excludes strings that names of refs can't start with; may be empty.
-	 * @return immutable list of refs whose names start with {@code prefix} and none
-	 *         of the strings in {@code exclude}.
-	 * @throws java.io.IOException the reference space cannot be accessed.
+	 * The default implementation is not efficient. Implementors of
+	 * {@link RefDatabase} should override this method directly if a better
+	 * implementation is possible.
+	 *
+	 * @param include
+	 *            string that names of refs should start with; may be empty.
+	 * @param excludes
+	 *            strings that names of refs can't start with; may be empty.
+	 * @return immutable list of refs whose names start with {@code prefix} and
+	 *         none of the strings in {@code exclude}.
+	 * @throws java.io.IOException
+	 *             the reference space cannot be accessed.
 	 * @since 5.11
 	 */
 	@NonNull
@@ -492,13 +506,14 @@
 	}
 
 	/**
-	 * If the ref database does not support fast inverse queries, it may
-	 * be advantageous to build a complete SHA1 to ref map in advance for
-	 * multiple uses. To let applications decide on this decision,
-	 * this function indicates whether the inverse map is available.
+	 * If the ref database does not support fast inverse queries, it may be
+	 * advantageous to build a complete SHA1 to ref map in advance for multiple
+	 * uses. To let applications decide on this decision, this function
+	 * indicates whether the inverse map is available.
 	 *
 	 * @return whether this RefDatabase supports fast inverse ref queries.
-	 * @throws IOException on I/O problems.
+	 * @throws IOException
+	 *             on I/O problems.
 	 * @since 5.6
 	 */
 	public boolean hasFastTipsWithSha1() throws IOException {
@@ -509,10 +524,10 @@
 	 * Check if any refs exist in the ref database.
 	 * <p>
 	 * This uses the same definition of refs as {@link #getRefs()}. In
-	 * particular, returns {@code false} in a new repository with no refs
-	 * under {@code refs/} and {@code HEAD} pointing to a branch yet to be
-	 * born, and returns {@code true} in a repository with no refs under
-	 * {@code refs/} and a detached {@code HEAD} pointing to history.
+	 * particular, returns {@code false} in a new repository with no refs under
+	 * {@code refs/} and {@code HEAD} pointing to a branch yet to be born, and
+	 * returns {@code true} in a repository with no refs under {@code refs/} and
+	 * a detached {@code HEAD} pointing to history.
 	 *
 	 * @return true if the database has refs.
 	 * @throws java.io.IOException
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefWriter.java
index c80d80f..d2c3f9d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefWriter.java
@@ -138,8 +138,7 @@
 		final StringWriter w = new StringWriter();
 		if (peeled) {
 			w.write(RefDirectory.PACKED_REFS_HEADER);
-			if (peeled)
-				w.write(RefDirectory.PACKED_REFS_PEELED);
+			w.write(RefDirectory.PACKED_REFS_PEELED);
 			w.write('\n');
 		}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/TextProgressMonitor.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/TextProgressMonitor.java
index 03a78eb..85aa0b6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/TextProgressMonitor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/TextProgressMonitor.java
@@ -17,6 +17,7 @@
 import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
 import java.io.Writer;
+import java.time.Duration;
 
 /**
  * A simple progress reporter printing on a stream.
@@ -46,49 +47,53 @@
 
 	/** {@inheritDoc} */
 	@Override
-	protected void onUpdate(String taskName, int workCurr) {
+	protected void onUpdate(String taskName, int workCurr, Duration duration) {
 		StringBuilder s = new StringBuilder();
-		format(s, taskName, workCurr);
+		format(s, taskName, workCurr, duration);
 		send(s);
 	}
 
 	/** {@inheritDoc} */
 	@Override
-	protected void onEndTask(String taskName, int workCurr) {
+	protected void onEndTask(String taskName, int workCurr, Duration duration) {
 		StringBuilder s = new StringBuilder();
-		format(s, taskName, workCurr);
+		format(s, taskName, workCurr, duration);
 		s.append("\n"); //$NON-NLS-1$
 		send(s);
 	}
 
-	private void format(StringBuilder s, String taskName, int workCurr) {
+	private void format(StringBuilder s, String taskName, int workCurr,
+			Duration duration) {
 		s.append("\r"); //$NON-NLS-1$
 		s.append(taskName);
 		s.append(": "); //$NON-NLS-1$
 		while (s.length() < 25)
 			s.append(' ');
 		s.append(workCurr);
+		appendDuration(s, duration);
 	}
 
 	/** {@inheritDoc} */
 	@Override
-	protected void onUpdate(String taskName, int cmp, int totalWork, int pcnt) {
+	protected void onUpdate(String taskName, int cmp, int totalWork, int pcnt,
+			Duration duration) {
 		StringBuilder s = new StringBuilder();
-		format(s, taskName, cmp, totalWork, pcnt);
+		format(s, taskName, cmp, totalWork, pcnt, duration);
 		send(s);
 	}
 
 	/** {@inheritDoc} */
 	@Override
-	protected void onEndTask(String taskName, int cmp, int totalWork, int pcnt) {
+	protected void onEndTask(String taskName, int cmp, int totalWork, int pcnt,
+			Duration duration) {
 		StringBuilder s = new StringBuilder();
-		format(s, taskName, cmp, totalWork, pcnt);
+		format(s, taskName, cmp, totalWork, pcnt, duration);
 		s.append("\n"); //$NON-NLS-1$
 		send(s);
 	}
 
 	private void format(StringBuilder s, String taskName, int cmp,
-			int totalWork, int pcnt) {
+			int totalWork, int pcnt, Duration duration) {
 		s.append("\r"); //$NON-NLS-1$
 		s.append(taskName);
 		s.append(": "); //$NON-NLS-1$
@@ -106,9 +111,10 @@
 		s.append(pcnt);
 		s.append("% ("); //$NON-NLS-1$
 		s.append(curStr);
-		s.append("/"); //$NON-NLS-1$
+		s.append('/');
 		s.append(endStr);
-		s.append(")"); //$NON-NLS-1$
+		s.append(')');
+		appendDuration(s, duration);
 	}
 
 	private void send(StringBuilder s) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ThreadSafeProgressMonitor.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ThreadSafeProgressMonitor.java
index 180fbdc..e553955 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ThreadSafeProgressMonitor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ThreadSafeProgressMonitor.java
@@ -158,6 +158,11 @@
 		pm.endTask();
 	}
 
+	@Override
+	public void showDuration(boolean enabled) {
+		pm.showDuration(enabled);
+	}
+
 	private boolean isMainThread() {
 		return Thread.currentThread() == mainThread;
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/patch/PatchApplier.java b/org.eclipse.jgit/src/org/eclipse/jgit/patch/PatchApplier.java
index ca8ea5d..98a2804 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/patch/PatchApplier.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/patch/PatchApplier.java
@@ -24,6 +24,7 @@
 import java.text.MessageFormat;
 import java.time.Instant;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
@@ -68,12 +69,14 @@
 import org.eclipse.jgit.treewalk.filter.AndTreeFilter;
 import org.eclipse.jgit.treewalk.filter.NotIgnoredFilter;
 import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
+import org.eclipse.jgit.util.FS;
 import org.eclipse.jgit.util.FS.ExecutionResult;
 import org.eclipse.jgit.util.FileUtils;
 import org.eclipse.jgit.util.IO;
 import org.eclipse.jgit.util.LfsFactory;
 import org.eclipse.jgit.util.LfsFactory.LfsInputStream;
 import org.eclipse.jgit.util.RawParseUtils;
+import org.eclipse.jgit.util.StringUtils;
 import org.eclipse.jgit.util.TemporaryBuffer;
 import org.eclipse.jgit.util.TemporaryBuffer.LocalFile;
 import org.eclipse.jgit.util.io.BinaryDeltaInputStream;
@@ -91,6 +94,9 @@
  */
 public class PatchApplier {
 
+	private static final byte[] NO_EOL = "\\ No newline at end of file" //$NON-NLS-1$
+			.getBytes(StandardCharsets.US_ASCII);
+
 	/** The tree before applying the patch. Only non-null for inCore operation. */
 	@Nullable
 	private final RevTree beforeTree;
@@ -180,7 +186,7 @@
 	public Result applyPatch(InputStream patchInput)
 			throws PatchFormatException, PatchApplyException {
 		Result result = new Result();
-		org.eclipse.jgit.patch.Patch p = new org.eclipse.jgit.patch.Patch();
+		Patch p = new Patch();
 		try (InputStream inStream = patchInput) {
 			p.parse(inStream);
 
@@ -188,12 +194,12 @@
 				throw new PatchFormatException(p.getErrors());
 			}
 
-			DirCache dirCache = (inCore()) ? DirCache.newInCore()
+			DirCache dirCache = inCore() ? DirCache.read(reader, beforeTree)
 					: repo.lockDirCache();
 
 			DirCacheBuilder dirCacheBuilder = dirCache.builder();
 			Set<String> modifiedPaths = new HashSet<>();
-			for (org.eclipse.jgit.patch.FileHeader fh : p.getFiles()) {
+			for (FileHeader fh : p.getFiles()) {
 				ChangeType type = fh.getChangeType();
 				switch (type) {
 				case ADD: {
@@ -345,8 +351,8 @@
 	 * @throws PatchApplyException
 	 */
 	private void apply(String pathWithOriginalContent, DirCache dirCache,
-			DirCacheBuilder dirCacheBuilder, @Nullable File f,
-			org.eclipse.jgit.patch.FileHeader fh) throws PatchApplyException {
+			DirCacheBuilder dirCacheBuilder, @Nullable File f, FileHeader fh)
+			throws PatchApplyException {
 		if (PatchType.BINARY.equals(fh.getPatchType())) {
 			// This patch type just says "something changed". We can't do
 			// anything with that.
@@ -484,7 +490,7 @@
 		}
 		dce.setLength(length);
 
-		try (LfsInputStream is = org.eclipse.jgit.util.LfsFactory.getInstance()
+		try (LfsInputStream is = LfsFactory.getInstance()
 				.applyCleanFilter(repo, input, length, lfsAttribute)) {
 			dce.setObjectId(inserter.insert(OBJ_BLOB, is.getLength(), is));
 		}
@@ -522,15 +528,13 @@
 			// conversion.
 			try (InputStream input = filterClean(repo, path,
 					fileStreamSupplier.load(), convertCrLf, filterCommand)) {
-				return new RawText(org.eclipse.jgit.util.IO
-						.readWholeStream(input, 0).array());
+				return new RawText(IO.readWholeStream(input, 0).array());
 			}
 		}
 		if (convertCrLf) {
 			try (InputStream input = EolStreamTypeUtil.wrapInputStream(
 					fileStreamSupplier.load(), EolStreamType.TEXT_LF)) {
-				return new RawText(org.eclipse.jgit.util.IO
-						.readWholeStream(input, 0).array());
+				return new RawText(IO.readWholeStream(input, 0).array());
 			}
 		}
 		if (inCore() && fileId.equals(ObjectId.zeroId())) {
@@ -547,12 +551,12 @@
 			input = EolStreamTypeUtil.wrapInputStream(input,
 					EolStreamType.TEXT_LF);
 		}
-		if (org.eclipse.jgit.util.StringUtils.isEmptyOrNull(filterCommand)) {
+		if (StringUtils.isEmptyOrNull(filterCommand)) {
 			return input;
 		}
 		if (FilterCommandRegistry.isRegistered(filterCommand)) {
-			LocalFile buffer = new org.eclipse.jgit.util.TemporaryBuffer.LocalFile(
-					null, inCoreSizeLimit);
+			LocalFile buffer = new TemporaryBuffer.LocalFile(null,
+					inCoreSizeLimit);
 			FilterCommand command = FilterCommandRegistry.createFilterCommand(
 					filterCommand, repository, input, buffer);
 			while (command.run() != -1) {
@@ -560,7 +564,7 @@
 			}
 			return buffer.openInputStreamWithAutoDestroy();
 		}
-		org.eclipse.jgit.util.FS fs = repository.getFS();
+		FS fs = repository.getFS();
 		ProcessBuilder filterProcessBuilder = fs.runInShell(filterCommand,
 				new String[0]);
 		filterProcessBuilder.directory(repository.getWorkTree());
@@ -577,14 +581,14 @@
 		if (rc != 0) {
 			throw new IOException(new FilterFailedException(rc, filterCommand,
 					path, result.getStdout().toByteArray(4096),
-					org.eclipse.jgit.util.RawParseUtils
+					RawParseUtils
 							.decode(result.getStderr().toByteArray(4096))));
 		}
 		return result.getStdout().openInputStreamWithAutoDestroy();
 	}
 
-	private boolean needsCrLfConversion(File f,
-			org.eclipse.jgit.patch.FileHeader fileHeader) throws IOException {
+	private boolean needsCrLfConversion(File f, FileHeader fileHeader)
+			throws IOException {
 		if (PatchType.GIT_BINARY.equals(fileHeader.getPatchType())) {
 			return false;
 		}
@@ -596,12 +600,11 @@
 		return false;
 	}
 
-	private static boolean hasCrLf(
-			org.eclipse.jgit.patch.FileHeader fileHeader) {
+	private static boolean hasCrLf(FileHeader fileHeader) {
 		if (PatchType.GIT_BINARY.equals(fileHeader.getPatchType())) {
 			return false;
 		}
-		for (org.eclipse.jgit.patch.HunkHeader header : fileHeader.getHunks()) {
+		for (HunkHeader header : fileHeader.getHunks()) {
 			byte[] buf = header.getBuffer();
 			int hunkEnd = header.getEndOffset();
 			int lineStart = header.getStartOffset();
@@ -702,15 +705,15 @@
 	 * @throws IOException
 	 * @throws UnsupportedOperationException
 	 */
-	private ContentStreamLoader applyBinary(String path, File f,
-			org.eclipse.jgit.patch.FileHeader fh, StreamSupplier inputSupplier,
-			ObjectId id) throws PatchApplyException, IOException,
+	private ContentStreamLoader applyBinary(String path, File f, FileHeader fh,
+			StreamSupplier inputSupplier, ObjectId id)
+			throws PatchApplyException, IOException,
 			UnsupportedOperationException {
 		if (!fh.getOldId().isComplete() || !fh.getNewId().isComplete()) {
 			throw new PatchApplyException(MessageFormat
 					.format(JGitText.get().applyBinaryOidTooShort, path));
 		}
-		org.eclipse.jgit.patch.BinaryHunk hunk = fh.getForwardBinaryHunk();
+		BinaryHunk hunk = fh.getForwardBinaryHunk();
 		// A BinaryHunk has the start at the "literal" or "delta" token. Data
 		// starts on the next line.
 		int start = RawParseUtils.nextLF(hunk.getBuffer(),
@@ -753,8 +756,7 @@
 		}
 	}
 
-	private ContentStreamLoader applyText(RawText rt,
-			org.eclipse.jgit.patch.FileHeader fh)
+	private ContentStreamLoader applyText(RawText rt, FileHeader fh)
 			throws IOException, PatchApplyException {
 		List<ByteBuffer> oldLines = new ArrayList<>(rt.size());
 		for (int i = 0; i < rt.size(); i++) {
@@ -764,7 +766,9 @@
 		int afterLastHunk = 0;
 		int lineNumberShift = 0;
 		int lastHunkNewLine = -1;
-		for (org.eclipse.jgit.patch.HunkHeader hh : fh.getHunks()) {
+		boolean lastWasRemoval = false;
+		boolean noNewLineAtEndOfNew = false;
+		for (HunkHeader hh : fh.getHunks()) {
 			// We assume hunks to be ordered
 			if (hh.getNewStartLine() <= lastHunkNewLine) {
 				throw new PatchApplyException(MessageFormat
@@ -852,17 +856,26 @@
 				if (!hunkLine.hasRemaining()) {
 					// Completely empty line; accept as empty context line
 					applyAt++;
+					lastWasRemoval = false;
 					continue;
 				}
 				switch (hunkLine.array()[hunkLine.position()]) {
 				case ' ':
 					applyAt++;
+					lastWasRemoval = false;
 					break;
 				case '-':
 					newLines.remove(applyAt);
+					lastWasRemoval = true;
 					break;
 				case '+':
 					newLines.add(applyAt++, slice(hunkLine, 1));
+					lastWasRemoval = false;
+					break;
+				case '\\':
+					if (!lastWasRemoval && isNoNewlineAtEnd(hunkLine)) {
+						noNewLineAtEndOfNew = true;
+					}
 					break;
 				default:
 					break;
@@ -870,12 +883,15 @@
 			}
 			afterLastHunk = applyAt;
 		}
-		if (!isNoNewlineAtEndOfFile(fh)) {
+		// If the last line should have a newline, add a null sentinel
+		if (lastHunkNewLine >= 0 && afterLastHunk == newLines.size()) {
+			// Last line came from the patch
+			if (!noNewLineAtEndOfNew) {
+				newLines.add(null);
+			}
+		} else if (!rt.isMissingNewlineAtEnd()) {
 			newLines.add(null);
 		}
-		if (!rt.isMissingNewlineAtEnd()) {
-			oldLines.add(null);
-		}
 
 		// We could check if old == new, but the short-circuiting complicates
 		// logic for inCore patching, so just write the new thing regardless.
@@ -933,21 +949,9 @@
 		return ByteBuffer.wrap(b.array(), newOffset, b.limit() - newOffset);
 	}
 
-	private boolean isNoNewlineAtEndOfFile(
-			org.eclipse.jgit.patch.FileHeader fh) {
-		List<? extends org.eclipse.jgit.patch.HunkHeader> hunks = fh.getHunks();
-		if (hunks == null || hunks.isEmpty()) {
-			return false;
-		}
-		org.eclipse.jgit.patch.HunkHeader lastHunk = hunks
-				.get(hunks.size() - 1);
-		byte[] buf = new byte[lastHunk.getEndOffset()
-				- lastHunk.getStartOffset()];
-		System.arraycopy(lastHunk.getBuffer(), lastHunk.getStartOffset(), buf,
-				0, buf.length);
-		RawText lhrt = new RawText(buf);
-		return lhrt.getString(lhrt.size() - 1)
-				.equals("\\ No newline at end of file"); //$NON-NLS-1$
+	private boolean isNoNewlineAtEnd(ByteBuffer hunkLine) {
+		return Arrays.equals(NO_EOL, 0, NO_EOL.length, hunkLine.array(),
+				hunkLine.position(), hunkLine.limit());
 	}
 
 	/**
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 6b644ce..b64c9ce 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommit.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommit.java
@@ -105,7 +105,12 @@
 
 	static final RevCommit[] NO_PARENTS = {};
 
-	private RevTree tree;
+	/**
+	 * Tree reference of the commit.
+	 *
+	 * @since 6.5
+	 */
+	protected RevTree tree;
 
 	/**
 	 * Avoid accessing this field directly. Use method
@@ -120,7 +125,15 @@
 
 	int inDegree;
 
-	private byte[] buffer;
+	/**
+	 * Raw unparsed commit body of the commit. Populated only
+	 * after {@link #parseCanonical(RevWalk, byte[])} with
+	 * {@link RevWalk#isRetainBody()} enable or after
+	 * {@link #parseBody(RevWalk)} and {@link #parse(RevWalk, byte[])}.
+	 *
+	 * @since 6.5.1
+	 */
+	protected byte[] buffer;
 
 	/**
 	 * Create a new commit reference.
@@ -657,6 +670,24 @@
 	}
 
 	/**
+	 * Get the distance of the commit from the root, as defined in
+	 * {@link org.eclipse.jgit.internal.storage.commitgraph.CommitGraph}
+	 * <p>
+	 * Generation number is
+	 * {@link org.eclipse.jgit.lib.Constants#COMMIT_GENERATION_UNKNOWN} when the
+	 * commit is not in the commit-graph. If a commit-graph file was written by
+	 * a version of Git that did not compute generation numbers, then those
+	 * commits in commit-graph will have generation number represented by
+	 * {@link org.eclipse.jgit.lib.Constants#COMMIT_GENERATION_NOT_COMPUTED}.
+	 *
+	 * @return the generation number
+	 * @since 6.5
+	 */
+	int getGeneration() {
+		return Constants.COMMIT_GENERATION_UNKNOWN;
+	}
+
+	/**
 	 * Reset this commit to allow another RevWalk with the same instances.
 	 * <p>
 	 * Subclasses <b>must</b> call <code>super.reset()</code> to ensure the
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommitCG.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommitCG.java
new file mode 100644
index 0000000..4d3664d
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommitCG.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2023, Tencent.
+ *
+ * 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 java.io.IOException;
+
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.internal.storage.commitgraph.CommitGraph;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+
+/**
+ * RevCommit parsed from
+ * {@link org.eclipse.jgit.internal.storage.commitgraph.CommitGraph}.
+ *
+ * @since 6.5
+ */
+class RevCommitCG extends RevCommit {
+
+	private final int graphPosition;
+
+	private int generation = Constants.COMMIT_GENERATION_UNKNOWN;
+
+	/**
+	 * Create a new commit reference.
+	 *
+	 * @param id
+	 *            object name for the commit.
+	 * @param graphPosition
+	 *            the position in the commit-graph of the object.
+	 */
+	protected RevCommitCG(AnyObjectId id, int graphPosition) {
+		super(id);
+		this.graphPosition = graphPosition;
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	void parseCanonical(RevWalk walk, byte[] raw) throws IOException {
+		if (walk.isRetainBody()) {
+			buffer = raw;
+		}
+		parseInGraph(walk);
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	void parseHeaders(RevWalk walk) throws MissingObjectException,
+			IncorrectObjectTypeException, IOException {
+		if (walk.isRetainBody()) {
+			super.parseBody(walk); // This parses header and body
+			return;
+		}
+		parseInGraph(walk);
+	}
+
+	private void parseInGraph(RevWalk walk) throws IOException {
+		CommitGraph graph = walk.commitGraph();
+		CommitGraph.CommitData data = graph.getCommitData(graphPosition);
+		if (data == null) {
+			// RevCommitCG was created because we got its graphPosition from
+			// commit-graph. If now the commit-graph doesn't know about it,
+			// something went wrong.
+			throw new IllegalStateException();
+		}
+		if (!walk.shallowCommitsInitialized) {
+			walk.initializeShallowCommits(this);
+		}
+
+		this.tree = walk.lookupTree(data.getTree());
+		this.commitTime = (int) data.getCommitTime();
+		this.generation = data.getGeneration();
+
+		if (getParents() == null) {
+			int[] pGraphList = data.getParents();
+			if (pGraphList.length == 0) {
+				this.parents = RevCommit.NO_PARENTS;
+			} else {
+				RevCommit[] pList = new RevCommit[pGraphList.length];
+				for (int i = 0; i < pList.length; i++) {
+					int graphPos = pGraphList[i];
+					ObjectId objId = graph.getObjectId(graphPos);
+					pList[i] = walk.lookupCommit(objId, graphPos);
+				}
+				this.parents = pList;
+			}
+		}
+		flags |= PARSED;
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	int getGeneration() {
+		return generation;
+	}
+}
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 8da36c5..9da7105 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
@@ -12,6 +12,8 @@
 
 package org.eclipse.jgit.revwalk;
 
+import static org.eclipse.jgit.internal.storage.commitgraph.CommitGraph.EMPTY;
+
 import java.io.IOException;
 import java.text.MessageFormat;
 import java.util.ArrayList;
@@ -30,6 +32,7 @@
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.lib.AnyObjectId;
 import org.eclipse.jgit.lib.AsyncObjectLoaderQueue;
+import org.eclipse.jgit.internal.storage.commitgraph.CommitGraph;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.MutableObjectId;
 import org.eclipse.jgit.lib.NullProgressMonitor;
@@ -189,6 +192,8 @@
 
 	private TreeFilter treeFilter;
 
+	private CommitGraph commitGraph;
+
 	private boolean retainBody = true;
 
 	private boolean rewriteParents = true;
@@ -237,6 +242,7 @@
 		filter = RevFilter.ALL;
 		treeFilter = TreeFilter.ALL;
 		this.closeReader = closeReader;
+		commitGraph = null;
 	}
 
 	/**
@@ -900,6 +906,29 @@
 	}
 
 	/**
+	 * This method is intended to be invoked only by {@link RevCommitCG}, in
+	 * order to give commit the correct graphPosition before accessing the
+	 * commit-graph. In this way, the headers of the commit can be obtained in
+	 * constant time.
+	 *
+	 * @param id
+	 *            name of the commit object.
+	 * @param graphPos
+	 *            the position in the commit-graph of the object.
+	 * @return reference to the commit object. Never null.
+	 * @since 6.5
+	 */
+	@NonNull
+	protected RevCommit lookupCommit(AnyObjectId id, int graphPos) {
+		RevCommit c = (RevCommit) objects.get(id);
+		if (c == null) {
+			c = createCommit(id, graphPos);
+			objects.add(c);
+		}
+		return c;
+	}
+
+	/**
 	 * Locate a reference to a tag without loading it.
 	 * <p>
 	 * The tag may or may not exist in the repository. It is impossible to tell
@@ -1136,6 +1165,26 @@
 	}
 
 	/**
+	 * Get the commit-graph.
+	 *
+	 * @return the commit-graph. Never null.
+	 * @since 6.5
+	 */
+	@NonNull
+	CommitGraph commitGraph() {
+		if (commitGraph == null) {
+			try {
+				commitGraph = reader != null
+						? reader.getCommitGraph().orElse(EMPTY)
+						: EMPTY;
+			} catch (IOException e) {
+				commitGraph = EMPTY;
+			}
+		}
+		return commitGraph;
+	}
+
+	/**
 	 * Asynchronous object parsing.
 	 *
 	 * @param objectIds
@@ -1650,6 +1699,13 @@
 	 * @return a new unparsed reference for the object.
 	 */
 	protected RevCommit createCommit(AnyObjectId id) {
+		return createCommit(id, commitGraph().findGraphPosition(id));
+	}
+
+	private RevCommit createCommit(AnyObjectId id, int graphPos) {
+		if (graphPos >= 0) {
+			return new RevCommitCG(id, graphPos);
+		}
 		return new RevCommit(id);
 	}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackConfig.java
index ff925db..a8180d1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackConfig.java
@@ -36,6 +36,7 @@
 import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_WAIT_PREVENT_RACYPACK;
 import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_WINDOW;
 import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_WINDOW_MEMORY;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_MIN_BYTES_OBJ_SIZE_INDEX;
 import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_PACK_SECTION;
 import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_PACK_KEPT_OBJECTS;
 import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_PRESERVE_OLD_PACKS;
@@ -249,6 +250,15 @@
 	public static final String[] DEFAULT_BITMAP_EXCLUDED_REFS_PREFIXES = new String[0];
 
 	/**
+	 * Default minimum size for an object to be included in the size index:
+	 * {@value}
+	 *
+	 * @see #setMinBytesForObjSizeIndex(int)
+	 * @since 6.5
+	 */
+	public static final int DEFAULT_MIN_BYTES_FOR_OBJ_SIZE_INDEX = -1;
+
+	/**
 	 * Default max time to spend during the search for reuse phase. This
 	 * optimization is disabled by default: {@value}
 	 *
@@ -318,6 +328,8 @@
 
 	private boolean singlePack;
 
+	private int minBytesForObjSizeIndex = DEFAULT_MIN_BYTES_FOR_OBJ_SIZE_INDEX;
+
 	/**
 	 * Create a default configuration.
 	 */
@@ -386,6 +398,7 @@
 		this.cutDeltaChains = cfg.cutDeltaChains;
 		this.singlePack = cfg.singlePack;
 		this.searchForReuseTimeout = cfg.searchForReuseTimeout;
+		this.minBytesForObjSizeIndex = cfg.minBytesForObjSizeIndex;
 	}
 
 	/**
@@ -1235,6 +1248,45 @@
 	}
 
 	/**
+	 * Minimum size of an object (inclusive) to be added in the object size
+	 * index.
+	 *
+	 * A negative value disables the writing of the object size index.
+	 *
+	 * @return minimum size an object must have to be included in the object
+	 *         index.
+	 * @since 6.5
+	 */
+	public int getMinBytesForObjSizeIndex() {
+		return minBytesForObjSizeIndex;
+	}
+
+	/**
+	 * Set minimum size an object must have to be included in the object size
+	 * index.
+	 *
+	 * A negative value disables the object index.
+	 *
+	 * @param minBytesForObjSizeIndex
+	 *            minimum size (inclusive) of an object to be included in the
+	 *            object size index. -1 disables the index.
+	 * @since 6.5
+	 */
+	public void setMinBytesForObjSizeIndex(int minBytesForObjSizeIndex) {
+		this.minBytesForObjSizeIndex = minBytesForObjSizeIndex;
+	}
+
+	/**
+	 * Should writers add an object size index when writing a pack.
+	 *
+	 * @return true to write an object-size index with the pack
+	 * @since 6.5
+	 */
+	public boolean isWriteObjSizeIndex() {
+		return this.minBytesForObjSizeIndex >= 0;
+	}
+
+	/**
 	 * Update properties by setting fields from the configuration.
 	 *
 	 * If a property's corresponding variable is not defined in the supplied
@@ -1316,6 +1368,9 @@
 		setMinSizePreventRacyPack(rc.getLong(CONFIG_PACK_SECTION,
 				CONFIG_KEY_MIN_SIZE_PREVENT_RACYPACK,
 				getMinSizePreventRacyPack()));
+		setMinBytesForObjSizeIndex(rc.getInt(CONFIG_PACK_SECTION,
+				CONFIG_KEY_MIN_BYTES_OBJ_SIZE_INDEX,
+				DEFAULT_MIN_BYTES_FOR_OBJ_SIZE_INDEX));
 		setPreserveOldPacks(rc.getBoolean(CONFIG_PACK_SECTION,
 				CONFIG_KEY_PRESERVE_OLD_PACKS, DEFAULT_PRESERVE_OLD_PACKS));
 		setPrunePreserved(rc.getBoolean(CONFIG_PACK_SECTION,
@@ -1355,6 +1410,8 @@
 		b.append(", searchForReuseTimeout") //$NON-NLS-1$
 				.append(getSearchForReuseTimeout());
 		b.append(", singlePack=").append(getSinglePack()); //$NON-NLS-1$
+		b.append(", minBytesForObjSizeIndex=") //$NON-NLS-1$
+				.append(getMinBytesForObjSizeIndex());
 		return b.toString();
 	}
 }
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 e0eb126..f02160e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java
@@ -90,6 +90,7 @@
 				.collect(Collectors.toList());
 	}
 
+	@SuppressWarnings("Finally")
 	void execute(ProgressMonitor monitor, FetchResult result,
 			String initialBranch)
 			throws NotSupportedException, TransportException {
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 c4129ff..e437f22 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java
@@ -281,6 +281,12 @@
 			return builder.build();
 		}
 
+		if (!PacketLineIn.isDelimiter(line)) {
+			throw new PackProtocolException(MessageFormat
+					.format(JGitText.get().unexpectedPacketLine, line));
+		}
+
+		line = pckIn.readString();
 		if (!line.equals("size")) { //$NON-NLS-1$
 			throw new PackProtocolException(MessageFormat
 					.format(JGitText.get().unexpectedPacketLine, line));
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SideBandProgressMonitor.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SideBandProgressMonitor.java
index 83e8bc2..3330860 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SideBandProgressMonitor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SideBandProgressMonitor.java
@@ -12,6 +12,7 @@
 
 import java.io.IOException;
 import java.io.OutputStream;
+import java.time.Duration;
 
 import org.eclipse.jgit.lib.BatchingProgressMonitor;
 import org.eclipse.jgit.lib.Constants;
@@ -29,48 +30,52 @@
 
 	/** {@inheritDoc} */
 	@Override
-	protected void onUpdate(String taskName, int workCurr) {
+	protected void onUpdate(String taskName, int workCurr, Duration duration) {
 		StringBuilder s = new StringBuilder();
-		format(s, taskName, workCurr);
+		format(s, taskName, workCurr, duration);
 		s.append("   \r"); //$NON-NLS-1$
 		send(s);
 	}
 
 	/** {@inheritDoc} */
 	@Override
-	protected void onEndTask(String taskName, int workCurr) {
+	protected void onEndTask(String taskName, int workCurr, Duration duration) {
 		StringBuilder s = new StringBuilder();
-		format(s, taskName, workCurr);
+		format(s, taskName, workCurr, duration);
 		s.append(", done\n"); //$NON-NLS-1$
 		send(s);
 	}
 
-	private void format(StringBuilder s, String taskName, int workCurr) {
+	private void format(StringBuilder s, String taskName, int workCurr,
+			Duration duration) {
 		s.append(taskName);
 		s.append(": "); //$NON-NLS-1$
 		s.append(workCurr);
+		appendDuration(s, duration);
 	}
 
 	/** {@inheritDoc} */
 	@Override
-	protected void onUpdate(String taskName, int cmp, int totalWork, int pcnt) {
+	protected void onUpdate(String taskName, int cmp, int totalWork, int pcnt,
+			Duration duration) {
 		StringBuilder s = new StringBuilder();
-		format(s, taskName, cmp, totalWork, pcnt);
+		format(s, taskName, cmp, totalWork, pcnt, duration);
 		s.append("   \r"); //$NON-NLS-1$
 		send(s);
 	}
 
 	/** {@inheritDoc} */
 	@Override
-	protected void onEndTask(String taskName, int cmp, int totalWork, int pcnt) {
+	protected void onEndTask(String taskName, int cmp, int totalWork, int pcnt,
+			Duration duration) {
 		StringBuilder s = new StringBuilder();
-		format(s, taskName, cmp, totalWork, pcnt);
+		format(s, taskName, cmp, totalWork, pcnt, duration);
 		s.append("\n"); //$NON-NLS-1$
 		send(s);
 	}
 
 	private void format(StringBuilder s, String taskName, int cmp,
-			int totalWork, int pcnt) {
+			int totalWork, int pcnt, Duration duration) {
 		s.append(taskName);
 		s.append(": "); //$NON-NLS-1$
 		if (pcnt < 100)
@@ -80,9 +85,10 @@
 		s.append(pcnt);
 		s.append("% ("); //$NON-NLS-1$
 		s.append(cmp);
-		s.append("/"); //$NON-NLS-1$
+		s.append('/');
 		s.append(totalWork);
-		s.append(")"); //$NON-NLS-1$
+		s.append(')');
+		appendDuration(s, duration);
 	}
 
 	private void send(StringBuilder s) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java
index 805166a..064201a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008, 2020 Google Inc. and others
+ * Copyright (C) 2008, 2023 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
@@ -118,6 +118,7 @@
 	private final boolean allowRefInWant;
 	private final boolean allowTipSha1InWant;
 	private final boolean allowReachableSha1InWant;
+	private final boolean allowAnySha1InWant;
 	private final boolean allowFilter;
 	private final boolean allowSidebandAll;
 
@@ -202,6 +203,8 @@
 				"uploadpack", "allowtipsha1inwant", false);
 		allowReachableSha1InWant = rc.getBoolean(
 				"uploadpack", "allowreachablesha1inwant", false);
+		allowAnySha1InWant = rc.getBoolean("uploadpack", "allowanysha1inwant",
+				false);
 		allowFilter = rc.getBoolean(
 				"uploadpack", "allowfilter", false);
 		protocolVersion = ProtocolVersion.parse(rc
@@ -284,6 +287,16 @@
 	}
 
 	/**
+	 * Whether to allow clients to request any SHA-1s
+	 *
+	 * @return allow clients to request any SHA-1s?
+	 * @since 6.5
+	 */
+	public boolean isAllowAnySha1InWant() {
+		return allowAnySha1InWant;
+	}
+
+	/**
 	 * @return true if clients are allowed to specify a "filter" line
 	 * @since 5.0
 	 */
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 9b40dfe..f245eae 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
@@ -681,6 +681,10 @@
 	 */
 	public void setTransferConfig(@Nullable TransferConfig tc) {
 		this.transferConfig = tc != null ? tc : new TransferConfig(db);
+		if (transferConfig.isAllowAnySha1InWant()) {
+			setRequestPolicy(RequestPolicy.ANY);
+			return;
+		}
 		if (transferConfig.isAllowTipSha1InWant()) {
 			setRequestPolicy(transferConfig.isAllowReachableSha1InWant()
 				? RequestPolicy.REACHABLE_COMMIT_TIP : RequestPolicy.TIP);
@@ -1386,6 +1390,9 @@
 		if (transferConfig.isAllowReceiveClientSID()) {
 			caps.add(OPTION_SESSION_ID);
 		}
+		if (transferConfig.isAdvertiseObjectInfo()) {
+			caps.add(COMMAND_OBJECT_INFO);
+		}
 
 		return caps;
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java
index d67fe07..ed8f450 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java
@@ -470,6 +470,7 @@
 		}
 	}
 
+	@SuppressWarnings("Finally")
 	private boolean downloadPackedObject(final ProgressMonitor monitor,
 			final AnyObjectId id) throws TransportException {
 		// Search for the object in a remote pack whose index we have,
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkPushConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkPushConnection.java
index 03ef852..a54fd8e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkPushConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkPushConnection.java
@@ -230,7 +230,7 @@
 				// offsets from appearing to clients.
 				//
 				dest.writeInfoPacks(packNames.keySet());
-				dest.deleteFile(idx.getPath());
+				dest.deleteFile(sanitizedPath(idx));
 			}
 
 			// Write the pack file, then the index, as readers look the
@@ -238,13 +238,13 @@
 			//
 			String wt = "Put " + pack.getName().substring(0, 12); //$NON-NLS-1$
 			try (OutputStream os = new BufferedOutputStream(
-					dest.writeFile(pack.getPath(), monitor,
+					dest.writeFile(sanitizedPath(pack), monitor,
 							wt + "." + pack.getPackExt().getExtension()))) { //$NON-NLS-1$
 				writer.writePack(monitor, monitor, os);
 			}
 
 			try (OutputStream os = new BufferedOutputStream(
-					dest.writeFile(idx.getPath(), monitor,
+					dest.writeFile(sanitizedPath(idx), monitor,
 							wt + "." + idx.getPackExt().getExtension()))) { //$NON-NLS-1$
 				writer.writeIndex(os);
 			}
@@ -269,7 +269,7 @@
 	private void safeDelete(File path) {
 		if (path != null) {
 			try {
-				dest.deleteFile(path.getPath());
+				dest.deleteFile(sanitizedPath(path));
 			} catch (IOException cleanupFailure) {
 				// Ignore the deletion failure. We probably are
 				// already failing and were just trying to pick
@@ -366,4 +366,13 @@
 		}
 		return updates.get(0).getRemoteName();
 	}
+
+	private static String sanitizedPath(File file) {
+		String path = file.getPath();
+		if (File.separatorChar != '/') {
+			path = path.replace(File.separatorChar, '/');
+		}
+		return path;
+	}
+
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/IO.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/IO.java
index 6d5694e..80877bb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/IO.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/IO.java
@@ -207,6 +207,25 @@
 	}
 
 	/**
+	 * Read from input until the entire byte array filled, or throw an exception
+	 * if stream ends first.
+	 *
+	 * @param fd
+	 *            input stream to read the data from.
+	 * @param dst
+	 *            buffer that must be fully populated
+	 * @throws EOFException
+	 *             the stream ended before dst was fully populated.
+	 * @throws java.io.IOException
+	 *             there was an error reading from the stream.
+	 * @since 6.5
+	 */
+	public static void readFully(InputStream fd, byte[] dst)
+			throws IOException {
+		readFully(fd, dst, 0, dst.length);
+	}
+
+	/**
 	 * Read as much of the array as possible from a channel.
 	 *
 	 * @param channel
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 5ced071..a8a7790 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
@@ -65,6 +65,21 @@
 
 	private static volatile Boolean isLinux;
 
+	private static final String GIT_TRACE_PERFORMANCE = "GIT_TRACE_PERFORMANCE"; //$NON-NLS-1$
+
+	private static final boolean performanceTrace = initPerformanceTrace();
+
+	private static boolean initPerformanceTrace() {
+		String val = System.getenv(GIT_TRACE_PERFORMANCE);
+		if (val == null) {
+			val = System.getenv(GIT_TRACE_PERFORMANCE);
+		}
+		if (val != null) {
+			return Boolean.valueOf(val).booleanValue();
+		}
+		return false;
+	}
+
 	static {
 		SystemReader r = new Default();
 		r.init();
@@ -560,6 +575,16 @@
 		return isLinux.booleanValue();
 	}
 
+	/**
+	 * Whether performance trace is enabled
+	 *
+	 * @return whether performance trace is enabled
+	 * @since 6.5
+	 */
+	public boolean isPerformanceTraceEnabled() {
+		return performanceTrace;
+	}
+
 	private String getOsName() {
 		return AccessController.doPrivileged(
 				(PrivilegedAction<String>) () -> getProperty("os.name") //$NON-NLS-1$
diff --git a/pom.xml b/pom.xml
index d271641..37c8613 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.4.1-SNAPSHOT</version>
+  <version>6.5.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.2.0.202206071550-r</jgit-last-release-version>
+    <jgit-last-release-version>6.4.0.202211300538-r</jgit-last-release-version>
     <ant-version>1.10.12</ant-version>
     <apache-sshd-version>2.9.2</apache-sshd-version>
     <jsch-version>0.1.55</jsch-version>
@@ -159,24 +159,23 @@
     <junit-version>4.13.2</junit-version>
     <test-fork-count>1C</test-fork-count>
     <args4j-version>2.33</args4j-version>
-    <commons-compress-version>1.21</commons-compress-version>
+    <commons-compress-version>1.22</commons-compress-version>
     <osgi-core-version>6.0.0</osgi-core-version>
     <servlet-api-version>4.0.0</servlet-api-version>
-    <jetty-version>10.0.6</jetty-version>
-    <japicmp-version>0.15.3</japicmp-version>
-    <httpclient-version>4.5.13</httpclient-version>
-    <httpcore-version>4.4.15</httpcore-version>
+    <jetty-version>10.0.13</jetty-version>
+    <japicmp-version>0.17.1</japicmp-version>
+    <httpclient-version>4.5.14</httpclient-version>
+    <httpcore-version>4.4.16</httpcore-version>
     <slf4j-version>1.7.30</slf4j-version>
-    <maven-javadoc-plugin-version>3.3.1</maven-javadoc-plugin-version>
-    <tycho-extras-version>2.6.0</tycho-extras-version>
-    <gson-version>2.9.1</gson-version>
+    <maven-javadoc-plugin-version>3.4.1</maven-javadoc-plugin-version>
+    <gson-version>2.10.1</gson-version>
     <bouncycastle-version>1.72</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>
-    <maven-surefire-plugin-version>3.0.0-M5</maven-surefire-plugin-version>
+    <spotbugs-maven-plugin-version>4.7.3.0</spotbugs-maven-plugin-version>
+    <maven-project-info-reports-plugin-version>3.4.2</maven-project-info-reports-plugin-version>
+    <maven-jxr-plugin-version>3.3.0</maven-jxr-plugin-version>
+    <maven-surefire-plugin-version>3.0.0-M8</maven-surefire-plugin-version>
     <maven-surefire-report-plugin-version>${maven-surefire-plugin-version}</maven-surefire-report-plugin-version>
-    <maven-compiler-plugin-version>3.8.1</maven-compiler-plugin-version>
+    <maven-compiler-plugin-version>3.10.1</maven-compiler-plugin-version>
     <plexus-compiler-version>2.12.1</plexus-compiler-version>
     <hamcrest-version>2.2</hamcrest-version>
     <assertj-version>3.20.2</assertj-version>
@@ -223,7 +222,7 @@
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-jar-plugin</artifactId>
-          <version>3.2.0</version>
+          <version>3.3.0</version>
           <configuration>
             <archive>
               <manifestEntries>
@@ -242,25 +241,25 @@
 
         <plugin>
           <artifactId>maven-clean-plugin</artifactId>
-          <version>3.1.0</version>
+          <version>3.2.0</version>
         </plugin>
 
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-shade-plugin</artifactId>
-          <version>3.2.4</version>
+          <version>3.4.1</version>
         </plugin>
 
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-antrun-plugin</artifactId>
-          <version>3.0.0</version>
+          <version>3.1.0</version>
         </plugin>
 
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-dependency-plugin</artifactId>
-          <version>3.2.0</version>
+          <version>3.5.0</version>
         </plugin>
 
         <plugin>
@@ -283,13 +282,14 @@
             <forkCount>${test-fork-count}</forkCount>
             <reuseForks>true</reuseForks>
             <argLine>@{argLine}</argLine>
+            <rerunFailingTestsCount>3</rerunFailingTestsCount>
           </configuration>
         </plugin>
 
         <plugin>
           <groupId>org.codehaus.mojo</groupId>
           <artifactId>build-helper-maven-plugin</artifactId>
-          <version>3.2.0</version>
+          <version>3.3.0</version>
         </plugin>
 
         <plugin>
@@ -312,7 +312,7 @@
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-pmd-plugin</artifactId>
-          <version>3.15.0</version>
+          <version>3.20.0</version>
           <configuration>
             <sourceEncoding>utf-8</sourceEncoding>
             <minimumTokens>100</minimumTokens>
@@ -335,22 +335,22 @@
         <plugin>
           <groupId>org.eclipse.cbi.maven.plugins</groupId>
           <artifactId>eclipse-jarsigner-plugin</artifactId>
-          <version>1.3.2</version>
+          <version>1.3.5</version>
         </plugin>
         <plugin>
           <groupId>org.jacoco</groupId>
           <artifactId>jacoco-maven-plugin</artifactId>
-          <version>0.8.7</version>
+          <version>0.8.8</version>
         </plugin>
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-site-plugin</artifactId>
-          <version>3.9.1</version>
+          <version>3.12.1</version>
           <dependencies>
             <dependency><!-- add support for ssh/scp -->
               <groupId>org.apache.maven.wagon</groupId>
-              <artifactId>wagon-ssh</artifactId>
-              <version>3.5.2</version>
+              <artifactId>wagon-ssh-external</artifactId>
+              <version>3.5.3</version>
             </dependency>
           </dependencies>
         </plugin>
@@ -372,12 +372,12 @@
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-deploy-plugin</artifactId>
-          <version>3.0.0-M1</version>
+          <version>3.0.0</version>
         </plugin>
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-install-plugin</artifactId>
-          <version>3.0.0-M1</version>
+          <version>3.1.0</version>
         </plugin>
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
@@ -387,12 +387,12 @@
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-resources-plugin</artifactId>
-          <version>3.2.0</version>
+          <version>3.3.0</version>
         </plugin>
         <plugin>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-maven-plugin</artifactId>
-          <version>2.5.4</version>
+          <version>2.7.7</version>
         </plugin>
         <plugin>
           <groupId>org.eclipse.dash</groupId>
@@ -406,7 +406,7 @@
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-enforcer-plugin</artifactId>
-        <version>3.0.0</version>
+        <version>3.1.0</version>
         <executions>
           <execution>
             <id>enforce-maven</id>