Merge branch 'stable-7.4'

* stable-7.4:
  InterruptTimer: don't use Yoda-style condition
  InterruptTimer: avoid expensive notify when begin is soon after end
  InterruptTimer: avoid unneeded notify for end()
  Disable MergeToolTest#testEmptyToolName
  Add InterruptTimer Tests
  Allow to discover bitmap on disk created after the packfile
  Prepare 5.13.6-SNAPSHOT builds
  JGit v5.13.5.202508271544-r
  Remove resolver option from target-platform-configuration
  Add missing release property to maven build
  Suppress API errors for minor API changes in service releases
  Remove unnecessary casts

Change-Id: I662bf9782e418a4d1c037fdd0bc3a02e0a8e5c44
diff --git a/.gitignore b/.gitignore
index 1671449..2fd1727 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,8 @@
 /.project
 /org.eclipse.jgit.benchmarks/dependency-reduced-pom.xml
+/tools/maven-central/out/
+/tools/maven-central/staging-deploy/
+/tools/maven-central/jreleaser.yml
 target/
 infer-out
 bazel-*
diff --git a/WORKSPACE b/WORKSPACE
index 17a6179..644e283 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -116,18 +116,18 @@
     sha1 = "09d9e7024535fb4a3f74367ba7e0a2f5093af638",
 )
 
-JNA_VERS = "5.17.0"
+JNA_VERS = "5.18.1"
 
 maven_jar(
     name = "jna",
     artifact = "net.java.dev.jna:jna:" + JNA_VERS,
-    sha1 = "33d12735bef894440780fce64f9758d420c7bae2",
+    sha1 = "b27ba04287cc4abe769642fe8318d39fc89bf937",
 )
 
 maven_jar(
     name = "jna-platform",
     artifact = "net.java.dev.jna:jna-platform:" + JNA_VERS,
-    sha1 = "a4934c44d25a9d8c2ddf4203affd20330cb3426f",
+    sha1 = "dd817f391efc492041c9ae91127527c13750a789",
 )
 
 maven_jar(
@@ -142,16 +142,18 @@
     sha1 = "a3fcc5d3c29b2b03433aa2d2f2d2c1b1638924a1",
 )
 
+SLF4J_VERS = "2.0.17"
+
 maven_jar(
     name = "log-api",
-    artifact = "org.slf4j:slf4j-api:1.7.36",
-    sha1 = "6c62681a2f655b49963a5983b8b0950a6120ae14",
+    artifact = "org.slf4j:slf4j-api:" + SLF4J_VERS,
+    sha1 = "d9e58ac9c7779ba3bf8142aff6c830617a7fe60f",
 )
 
 maven_jar(
     name = "slf4j-simple",
-    artifact = "org.slf4j:slf4j-simple:1.7.36",
-    sha1 = "a41f9cfe6faafb2eb83a1c7dd2d0dfd844e2a936",
+    artifact = "org.slf4j:slf4j-simple:" + SLF4J_VERS,
+    sha1 = "9872a3fd794ffe7b18d17747926a64d61526ca96",
 )
 
 maven_jar(
@@ -168,8 +170,8 @@
 
 maven_jar(
     name = "commons-lang3",
-    artifact = "org.apache.commons:commons-lang3:3.18.0",
-    sha1 = "fb14946f0e39748a6571de0635acbe44e7885491",
+    artifact = "org.apache.commons:commons-lang3:3.19.0",
+    sha1 = "d6524b169a6574cd253760c472d419b47bfd37e6",
 )
 
 maven_jar(
@@ -198,34 +200,34 @@
 
 maven_jar(
     name = "hamcrest",
-    artifact = "org.hamcrest:hamcrest:2.2",
-    sha1 = "1820c0968dba3a11a1b30669bb1f01978a91dedc",
+    artifact = "org.hamcrest:hamcrest:3.0",
+    sha1 = "8fd9b78a8e6a6510a078a9e30e9e86a6035cfaf7",
 )
 
 maven_jar(
     name = "mockito",
-    artifact = "org.mockito:mockito-core:5.19.0",
-    sha1 = "82b320935809e3c8ff92c2007dae5dff88d76f32",
+    artifact = "org.mockito:mockito-core:5.20.0",
+    sha1 = "a32f446f38acf636363c5693db6498047731b9e0",
 )
 
 maven_jar(
     name = "assertj-core",
-    artifact = "org.assertj:assertj-core:3.27.4",
-    sha1 = "65e62575f7e422df6d290dc1cb15f3c97108b2b1",
+    artifact = "org.assertj:assertj-core:3.27.6",
+    sha1 = "8f34ccd6808899ad1d0aac6a770b73191f2f2a53",
 )
 
-BYTE_BUDDY_VERSION = "1.17.7"
+BYTE_BUDDY_VERSION = "1.17.8"
 
 maven_jar(
     name = "bytebuddy",
     artifact = "net.bytebuddy:byte-buddy:" + BYTE_BUDDY_VERSION,
-    sha1 = "3856bfab61beb23e099a0d6629f2ba8de4b98ace",
+    sha1 = "af5735f63d00ca47a9375fae5c7471a36331c6ed",
 )
 
 maven_jar(
     name = "bytebuddy-agent",
     artifact = "net.bytebuddy:byte-buddy-agent:" + BYTE_BUDDY_VERSION,
-    sha1 = "fbf3d6d649ed37fc9e9c59480a05be0a26e3c2da",
+    sha1 = "f09415827a71be7ed621c7bd02550678f28bc81c",
 )
 
 maven_jar(
@@ -236,82 +238,82 @@
 
 maven_jar(
     name = "gson",
-    artifact = "com.google.code.gson:gson:2.13.1",
-    sha1 = "853ce06c11316b33a8eae5e9095da096a9528b8f",
+    artifact = "com.google.code.gson:gson:2.13.2",
+    sha1 = "48b8230771e573b54ce6e867a9001e75977fe78e",
 )
 
-JETTY_VER = "12.1.0"
+JETTY_VER = "12.1.3"
 
 maven_jar(
     name = "jetty-servlet",
     artifact = "org.eclipse.jetty.ee10:jetty-ee10-servlet:" + JETTY_VER,
-    sha1 = "56c5180952f23a6af4d8f7e34e84a838c7d284cb",
+    sha1 = "b2709dddac048fb03735c9ea85673dbfcec8bdfc",
 )
 
 maven_jar(
     name = "jetty-security",
     artifact = "org.eclipse.jetty:jetty-security:" + JETTY_VER,
-    sha1 = "5fb2a02cc342fd132e5a358ecbb4479aabf58a3a",
+    sha1 = "f40cb316ef0e9a168f59eaf0b2d0440b7adcb1f8",
 )
 
 maven_jar(
     name = "jetty-server",
     artifact = "org.eclipse.jetty:jetty-server:" + JETTY_VER,
-    sha1 = "a53a5d0d03aaeab03908ff854cdd5e9ee256efca",
+    sha1 = "7741aafcd7d6dc718d04d4cfb883982e361d6577",
 )
 
 maven_jar(
     name = "jetty-session",
     artifact = "org.eclipse.jetty:jetty-session:" + JETTY_VER,
-    sha1 = "be1be354522861b72fdaade01f798ad7f6ffbac4",
+    sha1 = "0837f081a357a2c835a96ceaa09445921a33b0e9",
 )
 
 maven_jar(
     name = "jetty-http",
     artifact = "org.eclipse.jetty:jetty-http:" + JETTY_VER,
-    sha1 = "d74935bc8eb25780453dfd0a2e93c2729b566e79",
+    sha1 = "3822fb03ec85e4157cf68c1ac661b5d06fd19b25",
 )
 
 maven_jar(
     name = "jetty-io",
     artifact = "org.eclipse.jetty:jetty-io:" + JETTY_VER,
-    sha1 = "611910afde7e9a2cfda3cad1ec1466cdba55e444",
+    sha1 = "2c46bf53f41e40df72ff457c40553da2ace7b956",
 )
 
 maven_jar(
     name = "jetty-util",
     artifact = "org.eclipse.jetty:jetty-util:" + JETTY_VER,
-    sha1 = "e0d96b47d0289006941757352c6fe7e5d9370c3b",
+    sha1 = "9606f6cc440419f62214ceaa2c8ab4b312baf6be",
 )
 
 maven_jar(
     name = "jetty-util-ajax",
     artifact = "org.eclipse.jetty:jetty-util-ajax:" + JETTY_VER,
-    sha1 = "26c44a954086af8518fcb311c94c5703f1d3093b",
+    sha1 = "d0b0a631434ca2cf5a913d2852893779bfecebf8",
 )
 
-BOUNCYCASTLE_VER = "1.81"
+BOUNCYCASTLE_VER = "1.82"
 
 maven_jar(
     name = "bcpg",
     artifact = "org.bouncycastle:bcpg-jdk18on:" + BOUNCYCASTLE_VER,
-    sha1 = "7cd50193dd6a080a203716d993f7a246c8d94291",
+    sha1 = "55cd7f445018b18119e3f2e67978fb39445b791c",
 )
 
 maven_jar(
     name = "bcprov",
     artifact = "org.bouncycastle:bcprov-jdk18on:" + BOUNCYCASTLE_VER,
-    sha1 = "d17c094daef57dbd80af71687a475aa6df7cbe54",
+    sha1 = "e1118397395d21909a1b7b15120d0c2a68d7fd0c",
 )
 
 maven_jar(
     name = "bcutil",
     artifact = "org.bouncycastle:bcutil-jdk18on:" + BOUNCYCASTLE_VER,
-    sha1 = "e2dd79395ab435094142b6aba219f35adcba0f01",
+    sha1 = "1850911d674c91ce6444783ff10478e2c6e9bbf9",
 )
 
 maven_jar(
     name = "bcpkix",
     artifact = "org.bouncycastle:bcpkix-jdk18on:" + BOUNCYCASTLE_VER,
-    sha1 = "819fd6f5d170c8b8bf8c5acc73816e9c36574042",
+    sha1 = "ad7b7155abac3e4e4f73579d5176c11f7659c560",
 )
diff --git a/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF
index fdad3a7..0157b21 100644
--- a/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF
@@ -5,14 +5,14 @@
 Automatic-Module-Name: org.eclipse.jgit.ant.test
 Bundle-SymbolicName: org.eclipse.jgit.ant.test
 Bundle-Vendor: %Bundle-Vendor
-Bundle-Version: 7.4.1.qualifier
+Bundle-Version: 7.5.0.qualifier
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: JavaSE-17
 Bundle-SCM: url=https://github.com/eclipse-jgit/jgit, connection=scm:git:https://eclipse.gerrithub.io/eclipse-jgit/jgit.git, developerConnection=scm:git:https://eclipse.gerrithub.io/a/eclipse-jgit/jgit.git
 Import-Package: org.apache.tools.ant,
- org.eclipse.jgit.ant.tasks;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.junit;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.lib;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.util;version="[7.4.1,7.5.0)",
- org.hamcrest.core;version="[1.1.0,3.0.0)",
+ org.eclipse.jgit.ant.tasks;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.junit;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.lib;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.util;version="[7.5.0,7.6.0)",
+ org.hamcrest;version="[3.0.0,4.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 39d8a03..517156a 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>7.4.1-SNAPSHOT</version>
+    <version>7.5.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.ant.test</artifactId>
diff --git a/org.eclipse.jgit.ant/META-INF/MANIFEST.MF b/org.eclipse.jgit.ant/META-INF/MANIFEST.MF
index 46cf048..5ba344a 100644
--- a/org.eclipse.jgit.ant/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ant/META-INF/MANIFEST.MF
@@ -3,14 +3,14 @@
 Bundle-Name: %Bundle-Name
 Automatic-Module-Name: org.eclipse.jgit.ant
 Bundle-SymbolicName: org.eclipse.jgit.ant
-Bundle-Version: 7.4.1.qualifier
+Bundle-Version: 7.5.0.qualifier
 Bundle-RequiredExecutionEnvironment: JavaSE-17
 Bundle-SCM: url=https://github.com/eclipse-jgit/jgit, connection=scm:git:https://eclipse.gerrithub.io/eclipse-jgit/jgit.git, developerConnection=scm:git:https://eclipse.gerrithub.io/a/eclipse-jgit/jgit.git
 Import-Package: org.apache.tools.ant,
-  org.eclipse.jgit.storage.file;version="[7.4.1,7.5.0)"
+  org.eclipse.jgit.storage.file;version="[7.5.0,7.6.0)"
 Bundle-Localization: OSGI-INF/l10n/plugin
 Bundle-Vendor: %Bundle-Vendor
-Export-Package: org.eclipse.jgit.ant;version="7.4.1",
- org.eclipse.jgit.ant.tasks;version="7.4.1";
+Export-Package: org.eclipse.jgit.ant;version="7.5.0",
+ org.eclipse.jgit.ant.tasks;version="7.5.0";
   uses:="org.apache.tools.ant,
    org.apache.tools.ant.types"
diff --git a/org.eclipse.jgit.ant/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.ant/META-INF/SOURCE-MANIFEST.MF
index 334dcf8..df6d400 100644
--- a/org.eclipse.jgit.ant/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.ant/META-INF/SOURCE-MANIFEST.MF
@@ -3,6 +3,6 @@
 Bundle-Name: org.eclipse.jgit.ant - Sources
 Bundle-SymbolicName: org.eclipse.jgit.ant.source
 Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 7.4.1.qualifier
+Bundle-Version: 7.5.0.qualifier
 Bundle-SCM: url=https://github.com/eclipse-jgit/jgit, connection=scm:git:https://eclipse.gerrithub.io/eclipse-jgit/jgit.git, developerConnection=scm:git:https://eclipse.gerrithub.io/a/eclipse-jgit/jgit.git
-Eclipse-SourceBundle: org.eclipse.jgit.ant;version="7.4.1.qualifier";roots="."
+Eclipse-SourceBundle: org.eclipse.jgit.ant;version="7.5.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.ant/pom.xml b/org.eclipse.jgit.ant/pom.xml
index 446b4c7..4c3d7e1 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>7.4.1-SNAPSHOT</version>
+    <version>7.5.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.ant</artifactId>
diff --git a/org.eclipse.jgit.archive/META-INF/MANIFEST.MF b/org.eclipse.jgit.archive/META-INF/MANIFEST.MF
index 0d01fae..26039e4 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: 7.4.1.qualifier
+Bundle-Version: 7.5.0.qualifier
 Bundle-Vendor: %Bundle-Vendor
 Bundle-Localization: OSGI-INF/l10n/plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-17
@@ -14,18 +14,18 @@
  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="[7.4.1,7.5.0)",
- org.eclipse.jgit.lib;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.nls;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.revwalk;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.util;version="[7.4.1,7.5.0)",
+ org.eclipse.jgit.api;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.lib;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.nls;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.revwalk;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.util;version="[7.5.0,7.6.0)",
  org.osgi.framework;version="[1.3.0,2.0.0)",
  org.tukaani.xz
 Bundle-ActivationPolicy: lazy
 Bundle-Activator: org.eclipse.jgit.archive.FormatActivator
-Export-Package: org.eclipse.jgit.archive;version="7.4.1";
+Export-Package: org.eclipse.jgit.archive;version="7.5.0";
   uses:="org.apache.commons.compress.archivers,
    org.osgi.framework,
    org.eclipse.jgit.api,
    org.eclipse.jgit.lib",
- org.eclipse.jgit.archive.internal;version="7.4.1";x-internal:=true
+ org.eclipse.jgit.archive.internal;version="7.5.0";x-internal:=true
diff --git a/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF
index 51e8ac8..22f6779 100644
--- a/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF
@@ -3,6 +3,6 @@
 Bundle-Name: org.eclipse.jgit.archive - Sources
 Bundle-SymbolicName: org.eclipse.jgit.archive.source
 Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 7.4.1.qualifier
+Bundle-Version: 7.5.0.qualifier
 Bundle-SCM: url=https://github.com/eclipse-jgit/jgit, connection=scm:git:https://eclipse.gerrithub.io/eclipse-jgit/jgit.git, developerConnection=scm:git:https://eclipse.gerrithub.io/a/eclipse-jgit/jgit.git
-Eclipse-SourceBundle: org.eclipse.jgit.archive;version="7.4.1.qualifier";roots="."
+Eclipse-SourceBundle: org.eclipse.jgit.archive;version="7.5.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.archive/pom.xml b/org.eclipse.jgit.archive/pom.xml
index c8c3c7c..fe567b0 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>7.4.1-SNAPSHOT</version>
+    <version>7.5.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.archive</artifactId>
diff --git a/org.eclipse.jgit.benchmarks/pom.xml b/org.eclipse.jgit.benchmarks/pom.xml
index dc24e6c..1b185c8 100644
--- a/org.eclipse.jgit.benchmarks/pom.xml
+++ b/org.eclipse.jgit.benchmarks/pom.xml
@@ -16,7 +16,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>7.4.1-SNAPSHOT</version>
+    <version>7.5.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.benchmarks</artifactId>
diff --git a/org.eclipse.jgit.coverage/pom.xml b/org.eclipse.jgit.coverage/pom.xml
index 581881f..a43893b 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>7.4.1-SNAPSHOT</version>
+    <version>7.5.0-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
 
@@ -27,88 +27,88 @@
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit</artifactId>
-      <version>7.4.1-SNAPSHOT</version>
+      <version>7.5.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.ant</artifactId>
-      <version>7.4.1-SNAPSHOT</version>
+      <version>7.5.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.archive</artifactId>
-      <version>7.4.1-SNAPSHOT</version>
+      <version>7.5.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.http.apache</artifactId>
-      <version>7.4.1-SNAPSHOT</version>
+      <version>7.5.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.http.server</artifactId>
-      <version>7.4.1-SNAPSHOT</version>
+      <version>7.5.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.lfs</artifactId>
-      <version>7.4.1-SNAPSHOT</version>
+      <version>7.5.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.lfs.server</artifactId>
-      <version>7.4.1-SNAPSHOT</version>
+      <version>7.5.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.pgm</artifactId>
-      <version>7.4.1-SNAPSHOT</version>
+      <version>7.5.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.ui</artifactId>
-      <version>7.4.1-SNAPSHOT</version>
+      <version>7.5.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.ssh.apache</artifactId>
-      <version>7.4.1-SNAPSHOT</version>
+      <version>7.5.0-SNAPSHOT</version>
     </dependency>
 
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.test</artifactId>
-      <version>7.4.1-SNAPSHOT</version>
+      <version>7.5.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.ant.test</artifactId>
-      <version>7.4.1-SNAPSHOT</version>
+      <version>7.5.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.http.test</artifactId>
-      <version>7.4.1-SNAPSHOT</version>
+      <version>7.5.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.pgm.test</artifactId>
-      <version>7.4.1-SNAPSHOT</version>
+      <version>7.5.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.lfs.test</artifactId>
-      <version>7.4.1-SNAPSHOT</version>
+      <version>7.5.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.lfs.server.test</artifactId>
-      <version>7.4.1-SNAPSHOT</version>
+      <version>7.5.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.ssh.apache.test</artifactId>
-      <version>7.4.1-SNAPSHOT</version>
+      <version>7.5.0-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 ee66caf..10544b6 100644
--- a/org.eclipse.jgit.gpg.bc.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.gpg.bc.test/META-INF/MANIFEST.MF
@@ -3,21 +3,21 @@
 Bundle-Name: %Bundle-Name
 Automatic-Module-Name: org.eclipse.jgit.gpg.bc.test
 Bundle-SymbolicName: org.eclipse.jgit.gpg.bc.test
-Bundle-Version: 7.4.1.qualifier
+Bundle-Version: 7.5.0.qualifier
 Bundle-Vendor: %Bundle-Vendor
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-17
 Bundle-SCM: url=https://github.com/eclipse-jgit/jgit, connection=scm:git:https://eclipse.gerrithub.io/eclipse-jgit/jgit.git, developerConnection=scm:git:https://eclipse.gerrithub.io/a/eclipse-jgit/jgit.git
-Require-Bundle: org.hamcrest.core;bundle-version="[1.3.0,2.0.0)"
 Import-Package: org.bouncycastle.asn1.cryptlib;version="[1.80.0,2.0.0)",
  org.bouncycastle.jce.provider;version="[1.80.0,2.0.0)",
  org.bouncycastle.openpgp;version="[1.80.0,2.0.0)",
  org.bouncycastle.openpgp.operator;version="[1.80.0,2.0.0)",
  org.bouncycastle.openpgp.operator.jcajce;version="[1.80.0,2.0.0)",
  org.bouncycastle.util.encoders;version="[1.80.0,2.0.0)",
- org.eclipse.jgit.gpg.bc.internal;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.gpg.bc.internal.keys;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.util.sha1;version="[7.4.1,7.5.0)",
+ org.eclipse.jgit.gpg.bc.internal;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.gpg.bc.internal.keys;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.util.sha1;version="[7.5.0,7.6.0)",
+ org.hamcrest;version="[3.0.0,4.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)"
diff --git a/org.eclipse.jgit.gpg.bc.test/pom.xml b/org.eclipse.jgit.gpg.bc.test/pom.xml
index 3114dfd..9bde228 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>7.4.1-SNAPSHOT</version>
+    <version>7.5.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.gpg.bc.test</artifactId>
diff --git a/org.eclipse.jgit.gpg.bc/META-INF/MANIFEST.MF b/org.eclipse.jgit.gpg.bc/META-INF/MANIFEST.MF
index ab9cdfe..6bff1c9 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="[7.4.1,7.5.0)"
+Fragment-Host: org.eclipse.jgit;bundle-version="[7.5.0,7.6.0)"
 Bundle-Vendor: %Bundle-Vendor
 Bundle-Localization: OSGI-INF/l10n/gpg_bc
-Bundle-Version: 7.4.1.qualifier
+Bundle-Version: 7.5.0.qualifier
 Bundle-RequiredExecutionEnvironment: JavaSE-17
 Bundle-SCM: url=https://github.com/eclipse-jgit/jgit, connection=scm:git:https://eclipse.gerrithub.io/eclipse-jgit/jgit.git, developerConnection=scm:git:https://eclipse.gerrithub.io/a/eclipse-jgit/jgit.git
 Import-Package: org.bouncycastle.asn1;version="[1.80.0,2.0.0)",
@@ -27,5 +27,5 @@
  org.bouncycastle.openpgp.operator.jcajce;version="[1.80.0,2.0.0)",
  org.bouncycastle.util.encoders;version="[1.80.0,2.0.0)",
  org.slf4j;version="[1.7.0,3.0.0)"
-Export-Package: org.eclipse.jgit.gpg.bc.internal;version="7.4.1";x-friends:="org.eclipse.jgit.gpg.bc.test",
- org.eclipse.jgit.gpg.bc.internal.keys;version="7.4.1";x-friends:="org.eclipse.jgit.gpg.bc.test"
+Export-Package: org.eclipse.jgit.gpg.bc.internal;version="7.5.0";x-friends:="org.eclipse.jgit.gpg.bc.test",
+ org.eclipse.jgit.gpg.bc.internal.keys;version="7.5.0";x-friends:="org.eclipse.jgit.gpg.bc.test"
diff --git a/org.eclipse.jgit.gpg.bc/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.gpg.bc/META-INF/SOURCE-MANIFEST.MF
index d325fcf..168f1c0 100644
--- a/org.eclipse.jgit.gpg.bc/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.gpg.bc/META-INF/SOURCE-MANIFEST.MF
@@ -3,6 +3,6 @@
 Bundle-Name: org.eclipse.jgit.gpg.bc - Sources
 Bundle-SymbolicName: org.eclipse.jgit.gpg.bc.source
 Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 7.4.1.qualifier
+Bundle-Version: 7.5.0.qualifier
 Bundle-SCM: url=https://github.com/eclipse-jgit/jgit, connection=scm:git:https://eclipse.gerrithub.io/eclipse-jgit/jgit.git, developerConnection=scm:git:https://eclipse.gerrithub.io/a/eclipse-jgit/jgit.git
-Eclipse-SourceBundle: org.eclipse.jgit.gpg.bc;version="7.4.1.qualifier";roots="."
+Eclipse-SourceBundle: org.eclipse.jgit.gpg.bc;version="7.5.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.gpg.bc/pom.xml b/org.eclipse.jgit.gpg.bc/pom.xml
index edcc6c2..3ffc6a5 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>7.4.1-SNAPSHOT</version>
+    <version>7.5.0-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 42a3856..4935bc7 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: 7.4.1.qualifier
+Bundle-Version: 7.5.0.qualifier
 Bundle-RequiredExecutionEnvironment: JavaSE-17
 Bundle-SCM: url=https://github.com/eclipse-jgit/jgit, connection=scm:git:https://eclipse.gerrithub.io/eclipse-jgit/jgit.git, developerConnection=scm:git:https://eclipse.gerrithub.io/a/eclipse-jgit/jgit.git
 Bundle-Localization: OSGI-INF/l10n/plugin
@@ -27,11 +27,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="[7.4.1,7.5.0)",
- org.eclipse.jgit.nls;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.transport.http;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.util;version="[7.4.1,7.5.0)"
-Export-Package: org.eclipse.jgit.transport.http.apache;version="7.4.1";
+ org.eclipse.jgit.annotations;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.nls;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.transport.http;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.util;version="[7.5.0,7.6.0)"
+Export-Package: org.eclipse.jgit.transport.http.apache;version="7.5.0";
   uses:="org.apache.http.client,
    org.eclipse.jgit.transport.http,
    org.apache.http.entity,
diff --git a/org.eclipse.jgit.http.apache/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.http.apache/META-INF/SOURCE-MANIFEST.MF
index d52f778..44ff896 100644
--- a/org.eclipse.jgit.http.apache/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.http.apache/META-INF/SOURCE-MANIFEST.MF
@@ -3,6 +3,6 @@
 Bundle-Name: org.eclipse.jgit.http.apache - Sources
 Bundle-SymbolicName: org.eclipse.jgit.http.apache.source
 Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 7.4.1.qualifier
+Bundle-Version: 7.5.0.qualifier
 Bundle-SCM: url=https://github.com/eclipse-jgit/jgit, connection=scm:git:https://eclipse.gerrithub.io/eclipse-jgit/jgit.git, developerConnection=scm:git:https://eclipse.gerrithub.io/a/eclipse-jgit/jgit.git
-Eclipse-SourceBundle: org.eclipse.jgit.http.apache;version="7.4.1.qualifier";roots="."
+Eclipse-SourceBundle: org.eclipse.jgit.http.apache;version="7.5.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.http.apache/pom.xml b/org.eclipse.jgit.http.apache/pom.xml
index 37d9d55..27ff965 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>7.4.1-SNAPSHOT</version>
+    <version>7.5.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.http.apache</artifactId>
diff --git a/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF
index 0c679ba..d80defe 100644
--- a/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF
@@ -3,31 +3,31 @@
 Bundle-Name: %Bundle-Name
 Automatic-Module-Name: org.eclipse.jgit.http.server
 Bundle-SymbolicName: org.eclipse.jgit.http.server
-Bundle-Version: 7.4.1.qualifier
+Bundle-Version: 7.5.0.qualifier
 Bundle-Localization: OSGI-INF/l10n/plugin
 Bundle-Vendor: %Bundle-Vendor
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: JavaSE-17
 Bundle-SCM: url=https://github.com/eclipse-jgit/jgit, connection=scm:git:https://eclipse.gerrithub.io/eclipse-jgit/jgit.git, developerConnection=scm:git:https://eclipse.gerrithub.io/a/eclipse-jgit/jgit.git
-Export-Package: org.eclipse.jgit.http.server;version="7.4.1",
- org.eclipse.jgit.http.server.glue;version="7.4.1";
+Export-Package: org.eclipse.jgit.http.server;version="7.5.0",
+ org.eclipse.jgit.http.server.glue;version="7.5.0";
   uses:="jakarta.servlet,
   	jakarta.servlet.http",
- org.eclipse.jgit.http.server.resolver;version="7.4.1";
+ org.eclipse.jgit.http.server.resolver;version="7.5.0";
   uses:="jakarta.servlet.http
    org.eclipse.jgit.transport.resolver,
    org.eclipse.jgit.lib,
    org.eclipse.jgit.transport,
 Import-Package: jakarta.servlet;version="[6.0.0,7.0.0)",
  jakarta.servlet.http;version="[6.0.0,7.0.0)",
- org.eclipse.jgit.annotations;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.errors;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal.storage.dfs;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal.storage.file;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal.transport.parser;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.lib;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.nls;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.revwalk;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.transport;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.transport.resolver;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.util;version="[7.4.1,7.5.0)"
+ org.eclipse.jgit.annotations;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.errors;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal.storage.file;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal.transport.parser;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.lib;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.nls;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.revwalk;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.transport;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.transport.resolver;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.util;version="[7.5.0,7.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 e07aab5..49164d5 100644
--- a/org.eclipse.jgit.http.server/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.http.server/META-INF/SOURCE-MANIFEST.MF
@@ -3,6 +3,6 @@
 Bundle-Name: org.eclipse.jgit.http.server - Sources
 Bundle-SymbolicName: org.eclipse.jgit.http.server.source
 Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 7.4.1.qualifier
+Bundle-Version: 7.5.0.qualifier
 Bundle-SCM: url=https://github.com/eclipse-jgit/jgit, connection=scm:git:https://eclipse.gerrithub.io/eclipse-jgit/jgit.git, developerConnection=scm:git:https://eclipse.gerrithub.io/a/eclipse-jgit/jgit.git
-Eclipse-SourceBundle: org.eclipse.jgit.http.server;version="7.4.1.qualifier";roots="."
+Eclipse-SourceBundle: org.eclipse.jgit.http.server;version="7.5.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.http.server/pom.xml b/org.eclipse.jgit.http.server/pom.xml
index 8050bad..bbd1fe1 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>7.4.1-SNAPSHOT</version>
+    <version>7.5.0-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 3c60ae2..8c4b4cb 100644
--- a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF
@@ -3,13 +3,11 @@
 Bundle-Name: %Bundle-Name
 Automatic-Module-Name: org.eclipse.jgit.http.test
 Bundle-SymbolicName: org.eclipse.jgit.http.test
-Bundle-Version: 7.4.1.qualifier
+Bundle-Version: 7.5.0.qualifier
 Bundle-Vendor: %Bundle-Vendor
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-17
 Bundle-SCM: url=https://github.com/eclipse-jgit/jgit, connection=scm:git:https://eclipse.gerrithub.io/eclipse-jgit/jgit.git, developerConnection=scm:git:https://eclipse.gerrithub.io/a/eclipse-jgit/jgit.git
-Require-Bundle: org.hamcrest.core;bundle-version="[1.3.0,2.0.0)",
- org.hamcrest.library;bundle-version="[1.3.0,2.0.0)"
 Import-Package: jakarta.servlet;version="[6.0.0,7.0.0)",
  jakarta.servlet.http;version="[6.0.0,7.0.0)",
  net.bytebuddy.agent;version="[1.9.0,2.0.0)",
@@ -30,26 +28,27 @@
  org.eclipse.jetty.util.component;version="[12.0.0,13.0.0)",
  org.eclipse.jetty.util.security;version="[12.0.0,13.0.0)",
  org.eclipse.jetty.util.thread;version="[12.0.0,13.0.0)",
- org.eclipse.jgit.api;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.errors;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.http.server;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.http.server.glue;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.http.server.resolver;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal.storage.dfs;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal.storage.file;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal.storage.reftable;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.junit;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.junit.http;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.lib;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.nls;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.revwalk;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.storage.file;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.transport;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.transport.http;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.transport.http.apache;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.transport.resolver;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.util;version="[7.4.1,7.5.0)",
+ org.eclipse.jgit.api;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.errors;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.http.server;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.http.server.glue;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.http.server.resolver;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal.storage.file;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal.storage.reftable;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.junit;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.junit.http;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.lib;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.nls;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.revwalk;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.storage.file;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.transport;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.transport.http;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.transport.http.apache;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.transport.resolver;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.util;version="[7.5.0,7.6.0)",
+ org.hamcrest;version="[3.0.0,4.0.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)",
diff --git a/org.eclipse.jgit.http.test/pom.xml b/org.eclipse.jgit.http.test/pom.xml
index 339213f..0b370cf 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>7.4.1-SNAPSHOT</version>
+    <version>7.5.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.http.test</artifactId>
diff --git a/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF
index 9262f16..ae4315b 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: 7.4.1.qualifier
+Bundle-Version: 7.5.0.qualifier
 Bundle-Localization: OSGI-INF/l10n/plugin
 Bundle-Vendor: %Bundle-Vendor
 Bundle-ActivationPolicy: lazy
@@ -23,17 +23,17 @@
  org.eclipse.jetty.util.component;version="[12.0.0,13.0.0)",
  org.eclipse.jetty.util.security;version="[12.0.0,13.0.0)",
  org.eclipse.jetty.util.ssl;version="[12.0.0,13.0.0)",
- org.eclipse.jgit.errors;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.http.server;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal.storage.file;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.junit;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.lib;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.revwalk;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.transport;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.transport.resolver;version="[7.4.1,7.5.0)",
+ org.eclipse.jgit.errors;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.http.server;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal.storage.file;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.junit;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.lib;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.revwalk;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.transport;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.transport.resolver;version="[7.5.0,7.6.0)",
  org.junit;version="[4.13,5.0.0)",
  org.slf4j.helpers;version="[1.7.0,3.0.0)"
-Export-Package: org.eclipse.jgit.junit.http;version="7.4.1";
+Export-Package: org.eclipse.jgit.junit.http;version="7.5.0";
   uses:="org.eclipse.jgit.transport,
    jakarta.servlet,
    jakarta.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 1fffbbb..c8c287c 100644
--- a/org.eclipse.jgit.junit.http/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.junit.http/META-INF/SOURCE-MANIFEST.MF
@@ -3,6 +3,6 @@
 Bundle-Name: org.eclipse.jgit.junit.http - Sources
 Bundle-SymbolicName: org.eclipse.jgit.junit.http.source
 Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 7.4.1.qualifier
+Bundle-Version: 7.5.0.qualifier
 Bundle-SCM: url=https://github.com/eclipse-jgit/jgit, connection=scm:git:https://eclipse.gerrithub.io/eclipse-jgit/jgit.git, developerConnection=scm:git:https://eclipse.gerrithub.io/a/eclipse-jgit/jgit.git
-Eclipse-SourceBundle: org.eclipse.jgit.junit.http;version="7.4.1.qualifier";roots="."
+Eclipse-SourceBundle: org.eclipse.jgit.junit.http;version="7.5.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.junit.http/pom.xml b/org.eclipse.jgit.junit.http/pom.xml
index cde5b2c..9b1eeaf 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>7.4.1-SNAPSHOT</version>
+    <version>7.5.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.junit.http</artifactId>
diff --git a/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF
index 6a20aac..ab02057 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: 7.4.1.qualifier
+Bundle-Version: 7.5.0.qualifier
 Bundle-Localization: OSGI-INF/l10n/plugin
 Bundle-Vendor: %Bundle-Vendor
 Bundle-ActivationPolicy: lazy
@@ -34,16 +34,16 @@
  org.apache.sshd.server.subsystem;version="[2.16.0,2.17.0)",
  org.apache.sshd.sftp;version="[2.16.0,2.17.0)",
  org.apache.sshd.sftp.server;version="[2.16.0,2.17.0)",
- org.eclipse.jgit.annotations;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.api;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.api.errors;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.errors;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.junit;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.lib;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.revwalk;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.transport;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.util;version="[7.4.1,7.5.0)",
+ org.eclipse.jgit.annotations;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.api;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.api.errors;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.errors;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.junit;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.lib;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.revwalk;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.transport;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.util;version="[7.5.0,7.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,3.0.0)"
-Export-Package: org.eclipse.jgit.junit.ssh;version="7.4.1"
+Export-Package: org.eclipse.jgit.junit.ssh;version="7.5.0"
diff --git a/org.eclipse.jgit.junit.ssh/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.junit.ssh/META-INF/SOURCE-MANIFEST.MF
index 498244c..b87fcbd 100644
--- a/org.eclipse.jgit.junit.ssh/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.junit.ssh/META-INF/SOURCE-MANIFEST.MF
@@ -3,6 +3,6 @@
 Bundle-Name: org.eclipse.jgit.junit.ssh - Sources
 Bundle-SymbolicName: org.eclipse.jgit.junit.ssh.source
 Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 7.4.1.qualifier
+Bundle-Version: 7.5.0.qualifier
 Bundle-SCM: url=https://github.com/eclipse-jgit/jgit, connection=scm:git:https://eclipse.gerrithub.io/eclipse-jgit/jgit.git, developerConnection=scm:git:https://eclipse.gerrithub.io/a/eclipse-jgit/jgit.git
-Eclipse-SourceBundle: org.eclipse.jgit.junit.ssh;version="7.4.1.qualifier";roots="."
+Eclipse-SourceBundle: org.eclipse.jgit.junit.ssh;version="7.5.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.junit.ssh/pom.xml b/org.eclipse.jgit.junit.ssh/pom.xml
index 51a2d4e..952ff2c 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>7.4.1-SNAPSHOT</version>
+    <version>7.5.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.junit.ssh</artifactId>
diff --git a/org.eclipse.jgit.junit/.settings/.api_filters b/org.eclipse.jgit.junit/.settings/.api_filters
deleted file mode 100644
index 2781530..0000000
--- a/org.eclipse.jgit.junit/.settings/.api_filters
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<component id="org.eclipse.jgit.junit" version="2">
-    <resource path="src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java" type="org.eclipse.jgit.junit.LocalDiskRepositoryTestCase">
-        <filter id="336658481">
-            <message_arguments>
-                <message_argument value="org.eclipse.jgit.junit.LocalDiskRepositoryTestCase"/>
-                <message_argument value="testRoot"/>
-            </message_arguments>
-        </filter>
-    </resource>
-</component>
diff --git a/org.eclipse.jgit.junit/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit/META-INF/MANIFEST.MF
index 10dce2c..11fe760 100644
--- a/org.eclipse.jgit.junit/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.junit/META-INF/MANIFEST.MF
@@ -3,38 +3,38 @@
 Bundle-Name: %Bundle-Name
 Automatic-Module-Name: org.eclipse.jgit.junit
 Bundle-SymbolicName: org.eclipse.jgit.junit
-Bundle-Version: 7.4.1.qualifier
+Bundle-Version: 7.5.0.qualifier
 Bundle-Localization: OSGI-INF/l10n/plugin
 Bundle-Vendor: %Bundle-Vendor
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: JavaSE-17
 Bundle-SCM: url=https://github.com/eclipse-jgit/jgit, connection=scm:git:https://eclipse.gerrithub.io/eclipse-jgit/jgit.git, developerConnection=scm:git:https://eclipse.gerrithub.io/a/eclipse-jgit/jgit.git
-Import-Package: org.eclipse.jgit.annotations;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.api;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.api.errors;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.dircache;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.errors;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal.storage.file;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal.storage.pack;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal.util;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.lib;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.merge;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.revwalk;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.storage.file;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.storage.pack;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.transport;version="7.4.1",
- org.eclipse.jgit.treewalk;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.treewalk.filter;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.util;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.util.io;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.util.time;version="[7.4.1,7.5.0)",
+Import-Package: org.eclipse.jgit.annotations;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.api;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.api.errors;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.dircache;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.errors;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal.storage.file;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal.util;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.lib;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.merge;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.revwalk;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.storage.file;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.storage.pack;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.transport;version="7.5.0",
+ org.eclipse.jgit.treewalk;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.treewalk.filter;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.util;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.util.io;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.util.time;version="[7.5.0,7.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,3.0.0)"
-Export-Package: org.eclipse.jgit.junit;version="7.4.1";
+Export-Package: org.eclipse.jgit.junit;version="7.5.0";
   uses:="org.eclipse.jgit.dircache,
    org.eclipse.jgit.lib,
    org.eclipse.jgit.revwalk,
@@ -47,4 +47,4 @@
    org.junit.runners.model,
    org.junit.runner,
    org.eclipse.jgit.util.time",
- org.eclipse.jgit.junit.time;version="7.4.1";uses:="org.eclipse.jgit.util.time"
+ org.eclipse.jgit.junit.time;version="7.5.0";uses:="org.eclipse.jgit.util.time"
diff --git a/org.eclipse.jgit.junit/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.junit/META-INF/SOURCE-MANIFEST.MF
index 364082e..0fc04b8 100644
--- a/org.eclipse.jgit.junit/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.junit/META-INF/SOURCE-MANIFEST.MF
@@ -3,6 +3,6 @@
 Bundle-Name: org.eclipse.jgit.junit - Sources
 Bundle-SymbolicName: org.eclipse.jgit.junit.source
 Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 7.4.1.qualifier
+Bundle-Version: 7.5.0.qualifier
 Bundle-SCM: url=https://github.com/eclipse-jgit/jgit, connection=scm:git:https://eclipse.gerrithub.io/eclipse-jgit/jgit.git, developerConnection=scm:git:https://eclipse.gerrithub.io/a/eclipse-jgit/jgit.git
-Eclipse-SourceBundle: org.eclipse.jgit.junit;version="7.4.1.qualifier";roots="."
+Eclipse-SourceBundle: org.eclipse.jgit.junit;version="7.5.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.junit/pom.xml b/org.eclipse.jgit.junit/pom.xml
index 20cd3eb..a6d3c43 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>7.4.1-SNAPSHOT</version>
+    <version>7.5.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.junit</artifactId>
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/FakeIndexFactory.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/FakeIndexFactory.java
index eb23bec..a1d28c9 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/FakeIndexFactory.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/FakeIndexFactory.java
@@ -188,7 +188,7 @@ public void verifyPackChecksum(String packFilePath) {
 
 		@Override
 		public ObjectId findObject(long offset) {
-			IndexObject indexObject = ridx.get(offset);
+			IndexObject indexObject = ridx.get(Long.valueOf(offset));
 			if (indexObject == null) {
 				return null;
 			}
@@ -198,7 +198,7 @@ public ObjectId findObject(long offset) {
 		@Override
 		public long findNextOffset(long offset, long maxOffset)
 				throws CorruptObjectException {
-			IndexObject o = ridx.get(offset);
+			IndexObject o = ridx.get(Long.valueOf(offset));
 			if (o == null) {
 				throw new CorruptObjectException("Invalid offset"); //$NON-NLS-1$
 			}
@@ -211,7 +211,7 @@ public long findNextOffset(long offset, long maxOffset)
 
 		@Override
 		public int findPosition(long offset) {
-			IndexObject indexObject = ridx.get(offset);
+			IndexObject indexObject = ridx.get(Long.valueOf(offset));
 			return byOffset.indexOf(indexObject);
 		}
 
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 02b7a69..cbe0465 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: 7.4.1.qualifier
+Bundle-Version: 7.5.0.qualifier
 Bundle-Vendor: %Bundle-Vendor
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-17
@@ -27,25 +27,25 @@
  org.eclipse.jetty.util.component;version="[12.0.0,13.0.0)",
  org.eclipse.jetty.util.security;version="[12.0.0,13.0.0)",
  org.eclipse.jetty.util.thread;version="[12.0.0,13.0.0)",
- org.eclipse.jgit.api;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.api.errors;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal.storage.file;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.junit;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.junit.http;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.lfs;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.lfs.errors;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.lfs.lib;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.lfs.server;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.lfs.server.fs;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.lfs.test;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.lib;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.revwalk;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.storage.file;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.transport;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.treewalk;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.treewalk.filter;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.util;version="[7.4.1,7.5.0)",
- org.hamcrest.core;version="[1.1.0,3.0.0)",
+ org.eclipse.jgit.api;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.api.errors;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal.storage.file;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.junit;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.junit.http;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.lfs;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.lfs.errors;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.lfs.lib;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.lfs.server;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.lfs.server.fs;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.lfs.test;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.lib;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.revwalk;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.storage.file;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.transport;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.treewalk;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.treewalk.filter;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.util;version="[7.5.0,7.6.0)",
+ org.hamcrest;version="[3.0.0,4.0.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)",
diff --git a/org.eclipse.jgit.lfs.server.test/pom.xml b/org.eclipse.jgit.lfs.server.test/pom.xml
index f00f85e..6d6088c 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>7.4.1-SNAPSHOT</version>
+    <version>7.5.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.lfs.server.test</artifactId>
diff --git a/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF
index 5ef7b85..518b51d 100644
--- a/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF
@@ -3,21 +3,21 @@
 Bundle-Name: %Bundle-Name
 Automatic-Module-Name: org.eclipse.jgit.lfs.server
 Bundle-SymbolicName: org.eclipse.jgit.lfs.server
-Bundle-Version: 7.4.1.qualifier
+Bundle-Version: 7.5.0.qualifier
 Bundle-Localization: OSGI-INF/l10n/plugin
 Bundle-Vendor: %Bundle-Vendor
 Bundle-RequiredExecutionEnvironment: JavaSE-17
 Bundle-SCM: url=https://github.com/eclipse-jgit/jgit, connection=scm:git:https://eclipse.gerrithub.io/eclipse-jgit/jgit.git, developerConnection=scm:git:https://eclipse.gerrithub.io/a/eclipse-jgit/jgit.git
-Export-Package: org.eclipse.jgit.lfs.server;version="7.4.1";
+Export-Package: org.eclipse.jgit.lfs.server;version="7.5.0";
   uses:="jakarta.servlet.http,
    org.eclipse.jgit.lfs.lib",
- org.eclipse.jgit.lfs.server.fs;version="7.4.1";
+ org.eclipse.jgit.lfs.server.fs;version="7.5.0";
   uses:="jakarta.servlet,
    jakarta.servlet.http,
    org.eclipse.jgit.lfs.server,
    org.eclipse.jgit.lfs.lib",
- org.eclipse.jgit.lfs.server.internal;version="7.4.1";x-internal:=true,
- org.eclipse.jgit.lfs.server.s3;version="7.4.1";
+ org.eclipse.jgit.lfs.server.internal;version="7.5.0";x-internal:=true,
+ org.eclipse.jgit.lfs.server.s3;version="7.5.0";
   uses:="org.eclipse.jgit.lfs.server,
    org.eclipse.jgit.lfs.lib"
 Import-Package: com.google.gson;version="[2.8.0,3.0.0)",
@@ -25,15 +25,15 @@
  jakarta.servlet.annotation;version="[6.0.0,7.0.0)",
  jakarta.servlet.http;version="[6.0.0,7.0.0)",
  org.apache.http;version="[4.3.0,5.0.0)",
- org.eclipse.jgit.annotations;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal.storage.file;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.lfs.errors;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.lfs.internal;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.lfs.lib;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.lib;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.nls;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.transport.http;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.transport.http.apache;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.util;version="[7.4.1,7.5.0)",
+ org.eclipse.jgit.annotations;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal.storage.file;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.lfs.errors;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.lfs.internal;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.lfs.lib;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.lib;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.nls;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.transport.http;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.transport.http.apache;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.util;version="[7.5.0,7.6.0)",
  org.slf4j;version="[1.7.0,3.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 9dcf4b8..9869530 100644
--- a/org.eclipse.jgit.lfs.server/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.lfs.server/META-INF/SOURCE-MANIFEST.MF
@@ -3,6 +3,6 @@
 Bundle-Name: org.eclipse.jgit.lfs.server - Sources
 Bundle-SymbolicName: org.eclipse.jgit.lfs.server.source
 Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 7.4.1.qualifier
+Bundle-Version: 7.5.0.qualifier
 Bundle-SCM: url=https://github.com/eclipse-jgit/jgit, connection=scm:git:https://eclipse.gerrithub.io/eclipse-jgit/jgit.git, developerConnection=scm:git:https://eclipse.gerrithub.io/a/eclipse-jgit/jgit.git
-Eclipse-SourceBundle: org.eclipse.jgit.lfs.server;version="7.4.1.qualifier";roots="."
+Eclipse-SourceBundle: org.eclipse.jgit.lfs.server;version="7.5.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.lfs.server/pom.xml b/org.eclipse.jgit.lfs.server/pom.xml
index 875b486..1c2fc51 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>7.4.1-SNAPSHOT</version>
+    <version>7.5.0-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 189cfc0..cc1b4cc 100644
--- a/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF
@@ -3,29 +3,29 @@
 Bundle-Name: %Bundle-Name
 Automatic-Module-Name: org.eclipse.jgit.lfs.test
 Bundle-SymbolicName: org.eclipse.jgit.lfs.test
-Bundle-Version: 7.4.1.qualifier
+Bundle-Version: 7.5.0.qualifier
 Bundle-Vendor: %Bundle-Vendor
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-17
 Bundle-SCM: url=https://github.com/eclipse-jgit/jgit, connection=scm:git:https://eclipse.gerrithub.io/eclipse-jgit/jgit.git, developerConnection=scm:git:https://eclipse.gerrithub.io/a/eclipse-jgit/jgit.git
-Import-Package: org.eclipse.jgit.api;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.attributes;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal.storage.dfs;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal.storage.file;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.junit;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.lfs;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.lfs.errors;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.lfs.internal;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.lfs.lib;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.lib;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.revwalk;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.transport;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.transport.http;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.treewalk;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.treewalk.filter;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.util;version="[7.4.1,7.5.0)",
- org.hamcrest.core;version="[1.1.0,3.0.0)",
+Import-Package: org.eclipse.jgit.api;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.attributes;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal.storage.file;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.junit;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.lfs;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.lfs.errors;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.lfs.internal;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.lfs.lib;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.lib;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.revwalk;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.transport;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.transport.http;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.treewalk;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.treewalk.filter;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.util;version="[7.5.0,7.6.0)",
+ org.hamcrest;version="[3.0.0,4.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="7.4.1";x-friends:="org.eclipse.jgit.lfs.server.test"
+Export-Package: org.eclipse.jgit.lfs.test;version="7.5.0";x-friends:="org.eclipse.jgit.lfs.server.test"
diff --git a/org.eclipse.jgit.lfs.test/pom.xml b/org.eclipse.jgit.lfs.test/pom.xml
index cdd9ee3..9f50190 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>7.4.1-SNAPSHOT</version>
+    <version>7.5.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.lfs.test</artifactId>
diff --git a/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF
index d6e2bec..1c311c4 100644
--- a/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF
@@ -3,33 +3,33 @@
 Bundle-Name: %Bundle-Name
 Automatic-Module-Name: org.eclipse.jgit.lfs
 Bundle-SymbolicName: org.eclipse.jgit.lfs
-Bundle-Version: 7.4.1.qualifier
+Bundle-Version: 7.5.0.qualifier
 Bundle-Localization: OSGI-INF/l10n/plugin
 Bundle-Vendor: %Bundle-Vendor
 Bundle-RequiredExecutionEnvironment: JavaSE-17
 Bundle-SCM: url=https://github.com/eclipse-jgit/jgit, connection=scm:git:https://eclipse.gerrithub.io/eclipse-jgit/jgit.git, developerConnection=scm:git:https://eclipse.gerrithub.io/a/eclipse-jgit/jgit.git
-Export-Package: org.eclipse.jgit.lfs;version="7.4.1",
- org.eclipse.jgit.lfs.errors;version="7.4.1",
- org.eclipse.jgit.lfs.internal;version="7.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="7.4.1"
+Export-Package: org.eclipse.jgit.lfs;version="7.5.0",
+ org.eclipse.jgit.lfs.errors;version="7.5.0",
+ org.eclipse.jgit.lfs.internal;version="7.5.0";x-friends:="org.eclipse.jgit.lfs.test,org.eclipse.jgit.lfs.server.fs,org.eclipse.jgit.lfs.server",
+ org.eclipse.jgit.lfs.lib;version="7.5.0"
 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="[7.4.1,7.5.0)";resolution:=optional,
- org.eclipse.jgit.api.errors;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.attributes;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.diff;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.dircache;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.errors;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.hooks;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal.storage.file;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.lib;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.nls;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.revwalk;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.storage.file;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.storage.pack;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.transport;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.transport.http;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.treewalk;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.treewalk.filter;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.util;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.util.io;version="[7.4.1,7.5.0)"
+ org.eclipse.jgit.annotations;version="[7.5.0,7.6.0)";resolution:=optional,
+ org.eclipse.jgit.api.errors;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.attributes;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.diff;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.dircache;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.errors;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.hooks;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal.storage.file;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.lib;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.nls;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.revwalk;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.storage.file;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.storage.pack;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.transport;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.transport.http;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.treewalk;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.treewalk.filter;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.util;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.util.io;version="[7.5.0,7.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 8da8f95..9e7ccc5 100644
--- a/org.eclipse.jgit.lfs/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.lfs/META-INF/SOURCE-MANIFEST.MF
@@ -3,6 +3,6 @@
 Bundle-Name: org.eclipse.jgit.lfs - Sources
 Bundle-SymbolicName: org.eclipse.jgit.lfs.source
 Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 7.4.1.qualifier
+Bundle-Version: 7.5.0.qualifier
 Bundle-SCM: url=https://github.com/eclipse-jgit/jgit, connection=scm:git:https://eclipse.gerrithub.io/eclipse-jgit/jgit.git, developerConnection=scm:git:https://eclipse.gerrithub.io/a/eclipse-jgit/jgit.git
-Eclipse-SourceBundle: org.eclipse.jgit.lfs;version="7.4.1.qualifier";roots="."
+Eclipse-SourceBundle: org.eclipse.jgit.lfs;version="7.5.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.lfs/pom.xml b/org.eclipse.jgit.lfs/pom.xml
index fe65299..f08df55 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>7.4.1-SNAPSHOT</version>
+    <version>7.5.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.lfs</artifactId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml
index 06d1578..70dba10 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="7.4.1.qualifier"
+      version="7.5.0.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml
index ff19611..9ba72fe 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>7.4.1-SNAPSHOT</version>
+    <version>7.5.0-SNAPSHOT</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.gpg.bc.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.gpg.bc.feature/feature.xml
index e56b452..8c5702f 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="7.4.1.qualifier"
+      version="7.5.0.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
@@ -23,7 +23,7 @@
    </url>
 
    <requires>
-      <import plugin="org.eclipse.jgit" version="7.4.1" match="equivalent"/>
+      <import plugin="org.eclipse.jgit" version="7.5.0" 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 0727a43..a69fdad 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>7.4.1-SNAPSHOT</version>
+    <version>7.5.0-SNAPSHOT</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml
index 631b721..0e2a1a0 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="7.4.1.qualifier"
+      version="7.5.0.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
@@ -23,7 +23,7 @@
    </url>
 
    <requires>
-      <import plugin="org.eclipse.jgit" version="7.4.1" match="equivalent"/>
+      <import plugin="org.eclipse.jgit" version="7.5.0" match="equivalent"/>
    </requires>
 
    <plugin
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml
index dbc8397..e858ec1 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>7.4.1-SNAPSHOT</version>
+    <version>7.5.0-SNAPSHOT</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml
index bb6bfea..a13a299 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="7.4.1.qualifier"
+      version="7.5.0.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
@@ -24,7 +24,7 @@
 
    <requires>
       <import plugin="com.jcraft.jsch"/>
-      <import plugin="org.eclipse.jgit" version="7.4.1" match="equivalent"/>
+      <import plugin="org.eclipse.jgit" version="7.5.0" match="equivalent"/>
    </requires>
 
    <plugin
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml
index ac8dbb5..0e695de 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>7.4.1-SNAPSHOT</version>
+    <version>7.5.0-SNAPSHOT</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.xml
index decb642..7b8e62e 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="7.4.1.qualifier"
+      version="7.5.0.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
@@ -23,7 +23,7 @@
    </url>
 
    <requires>
-      <import feature="org.eclipse.jgit" version="7.4.1" match="equivalent"/>
+      <import feature="org.eclipse.jgit" version="7.5.0" match="equivalent"/>
    </requires>
 
    <plugin
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/pom.xml
index 7cc8c01..4679767 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>7.4.1-SNAPSHOT</version>
+    <version>7.5.0-SNAPSHOT</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml
index f06516e..83ae321 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="7.4.1.qualifier"
+      version="7.5.0.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
@@ -35,9 +35,9 @@
          version="0.0.0"/>
 
    <requires>
-      <import feature="org.eclipse.jgit" version="7.4.1" match="equivalent"/>
-      <import feature="org.eclipse.jgit.lfs" version="7.4.1" match="equivalent"/>
-      <import feature="org.eclipse.jgit.ssh.apache" version="7.4.1" match="equivalent"/>
+      <import feature="org.eclipse.jgit" version="7.5.0" match="equivalent"/>
+      <import feature="org.eclipse.jgit.lfs" version="7.5.0" match="equivalent"/>
+      <import feature="org.eclipse.jgit.ssh.apache" version="7.5.0" match="equivalent"/>
    </requires>
 
    <plugin
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml
index 71c8a39..f3a73b1 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>7.4.1-SNAPSHOT</version>
+    <version>7.5.0-SNAPSHOT</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml
index eef699c..08294c7 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml
@@ -234,6 +234,24 @@
    <bundle id="org.osgi.service.cm.source">
       <category name="JGit-dependency-bundles"/>
    </bundle>
+   <bundle id="org.objectweb.asm">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
+   <bundle id="org.objectweb.asm.commons">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
+   <bundle id="org.objectweb.asm.util">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
+   <bundle id="org.objectweb.asm.tree">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
+   <bundle id="org.objectweb.asm.tree.analysis">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
+   <bundle id="org.apache.aries.spifly.dynamic.bundle">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
    <category-def name="JGit" label="Java implementation of Git">
       <description>
          Java implementation of Git
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 1e5a9b4..848f46c 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>7.4.1-SNAPSHOT</version>
+    <version>7.5.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.repository</artifactId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml
index eb2494b..1221eb4 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="7.4.1.qualifier"
+      version="7.5.0.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
@@ -23,7 +23,7 @@
    </url>
 
    <requires>
-      <import feature="org.eclipse.jgit" version="7.4.1" match="equivalent"/>
+      <import feature="org.eclipse.jgit" version="7.5.0" match="equivalent"/>
    </requires>
 
    <plugin
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml
index 11ccb15..ada5649 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>7.4.1-SNAPSHOT</version>
+    <version>7.5.0-SNAPSHOT</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
@@ -30,7 +30,7 @@
     <dependency>
       <groupId>org.eclipse.jgit.feature</groupId>
       <artifactId>org.eclipse.jgit</artifactId>
-      <version>7.4.1-SNAPSHOT</version>
+      <version>7.5.0-SNAPSHOT</version>
     </dependency>
   </dependencies>
 
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/feature.xml
index 09c8938..096558c 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="7.4.1.qualifier"
+      version="7.5.0.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
@@ -23,7 +23,7 @@
    </url>
 
    <requires>
-      <import feature="org.eclipse.jgit" version="7.4.1" match="equivalent"/>
+      <import feature="org.eclipse.jgit" version="7.5.0" 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 d0988b7..9b92cc8 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>7.4.1-SNAPSHOT</version>
+    <version>7.5.0-SNAPSHOT</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.jsch.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.jsch.feature/feature.xml
index 7617dbd..32a48f2 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="7.4.1.qualifier"
+      version="7.5.0.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
@@ -23,7 +23,7 @@
    </url>
 
    <requires>
-      <import plugin="org.eclipse.jgit" version="7.4.1" match="equivalent"/>
+      <import plugin="org.eclipse.jgit" version="7.5.0" 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 ad87d14..57fc61a 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>7.4.1-SNAPSHOT</version>
+    <version>7.5.0-SNAPSHOT</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.32.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.32.tpd
deleted file mode 100644
index b8574c7..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.32.tpd
+++ /dev/null
@@ -1,8 +0,0 @@
-target "jgit-4.32" with source configurePhase
-
-include "orbit/orbit-4.32.tpd"
-include "maven/dependencies.tpd"
-
-location "https://download.eclipse.org/staging/2024-06/" {
-	org.eclipse.osgi lazy
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.33.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.33.target
deleted file mode 100644
index eb2f21f..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.33.target
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<?pde?>
-<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.33" sequenceNumber="1756150983">
-  <locations>
-    <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
-      <unit id="com.jcraft.jsch" version="0.1.55.v20230916-1400"/>
-      <unit id="com.jcraft.jsch.source" version="0.1.55.v20230916-1400"/>
-      <unit id="com.jcraft.jzlib" version="1.1.3.v20230916-1400"/>
-      <unit id="com.jcraft.jzlib.source" version="1.1.3.v20230916-1400"/>
-      <unit id="org.apache.ant" version="1.10.14.v20230922-1200"/>
-      <unit id="org.apache.ant.source" version="1.10.14.v20230922-1200"/>
-      <unit id="org.apache.httpcomponents.httpclient" version="4.5.14"/>
-      <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14"/>
-      <unit id="org.apache.httpcomponents.httpcore" version="4.4.16"/>
-      <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16"/>
-      <unit id="org.hamcrest.core" version="1.3.0.v20230809-1000"/>
-      <unit id="org.hamcrest.core.source" version="1.3.0.v20230809-1000"/>
-      <unit id="org.hamcrest.library" version="1.3.0.v20230809-1000"/>
-      <unit id="org.hamcrest.library.source" version="1.3.0.v20230809-1000"/>
-      <unit id="org.junit" version="4.13.2.v20230809-1000"/>
-      <unit id="org.junit.source" version="4.13.2.v20230809-1000"/>
-      <unit id="org.objenesis" version="3.4.0"/>
-      <unit id="org.objenesis.source" version="3.4.0"/>
-      <unit id="org.osgi.service.cm" version="1.6.1.202109301733"/>
-      <unit id="org.osgi.service.cm.source" version="1.6.1.202109301733"/>
-      <repository location="https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2024-09"/>
-    </location>
-    <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
-      <unit id="org.eclipse.osgi" version="0.0.0"/>
-      <repository location="https://download.eclipse.org/releases/2024-09/"/>
-    </location>
-    <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="xz">
-      <dependencies>
-        <dependency>
-          <groupId>org.tukaani</groupId>
-          <artifactId>xz</artifactId>
-          <version>1.10</version>
-          <type>jar</type>
-        </dependency>
-      </dependencies>
-    </location>
-    <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="slf4j">
-      <dependencies>
-        <dependency>
-          <groupId>org.slf4j</groupId>
-          <artifactId>slf4j-api</artifactId>
-          <version>1.7.36</version>
-          <type>jar</type>
-        </dependency>
-        <dependency>
-          <groupId>org.slf4j</groupId>
-          <artifactId>slf4j-simple</artifactId>
-          <version>1.7.36</version>
-          <type>jar</type>
-        </dependency>
-      </dependencies>
-    </location>
-    <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="sshd">
-      <dependencies>
-        <dependency>
-          <groupId>org.apache.sshd</groupId>
-          <artifactId>sshd-osgi</artifactId>
-          <version>2.16.0</version>
-          <type>jar</type>
-        </dependency>
-        <dependency>
-          <groupId>org.apache.sshd</groupId>
-          <artifactId>sshd-sftp</artifactId>
-          <version>2.16.0</version>
-          <type>jar</type>
-        </dependency>
-      </dependencies>
-    </location>
-    <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="mockito">
-      <dependencies>
-        <dependency>
-          <groupId>org.mockito</groupId>
-          <artifactId>mockito-core</artifactId>
-          <version>5.19.0</version>
-          <type>jar</type>
-        </dependency>
-      </dependencies>
-    </location>
-    <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jna">
-      <dependencies>
-        <dependency>
-          <groupId>net.java.dev.jna</groupId>
-          <artifactId>jna</artifactId>
-          <version>5.17.0</version>
-          <type>jar</type>
-        </dependency>
-        <dependency>
-          <groupId>net.java.dev.jna</groupId>
-          <artifactId>jna-platform</artifactId>
-          <version>5.17.0</version>
-          <type>jar</type>
-        </dependency>
-      </dependencies>
-    </location>
-    <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jetty">
-      <dependencies>
-        <dependency>
-          <groupId>org.eclipse.jetty.ee10</groupId>
-          <artifactId>jetty-ee10-servlet</artifactId>
-          <version>12.1.0</version>
-          <type>jar</type>
-        </dependency>
-        <dependency>
-          <groupId>org.eclipse.jetty</groupId>
-          <artifactId>jetty-http</artifactId>
-          <version>12.1.0</version>
-          <type>jar</type>
-        </dependency>
-        <dependency>
-          <groupId>org.eclipse.jetty</groupId>
-          <artifactId>jetty-io</artifactId>
-          <version>12.1.0</version>
-          <type>jar</type>
-        </dependency>
-        <dependency>
-          <groupId>org.eclipse.jetty</groupId>
-          <artifactId>jetty-security</artifactId>
-          <version>12.1.0</version>
-          <type>jar</type>
-        </dependency>
-        <dependency>
-          <groupId>org.eclipse.jetty</groupId>
-          <artifactId>jetty-server</artifactId>
-          <version>12.1.0</version>
-          <type>jar</type>
-        </dependency>
-        <dependency>
-          <groupId>org.eclipse.jetty</groupId>
-          <artifactId>jetty-session</artifactId>
-          <version>12.1.0</version>
-          <type>jar</type>
-        </dependency>
-        <dependency>
-          <groupId>org.eclipse.jetty</groupId>
-          <artifactId>jetty-util</artifactId>
-          <version>12.1.0</version>
-          <type>jar</type>
-        </dependency>
-        <dependency>
-          <groupId>org.eclipse.jetty</groupId>
-          <artifactId>jetty-util-ajax</artifactId>
-          <version>12.1.0</version>
-          <type>jar</type>
-        </dependency>
-        <dependency>
-          <groupId>jakarta.servlet</groupId>
-          <artifactId>jakarta.servlet-api</artifactId>
-          <version>6.1.0</version>
-          <type>jar</type>
-        </dependency>
-      </dependencies>
-    </location>
-    <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="javaewah">
-      <dependencies>
-        <dependency>
-          <groupId>com.googlecode.javaewah</groupId>
-          <artifactId>JavaEWAH</artifactId>
-          <version>1.2.3</version>
-          <type>jar</type>
-        </dependency>
-      </dependencies>
-    </location>
-    <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="hamcrest">
-      <dependencies>
-        <dependency>
-          <groupId>org.hamcrest</groupId>
-          <artifactId>hamcrest</artifactId>
-          <version>2.2</version>
-          <type>jar</type>
-        </dependency>
-      </dependencies>
-    </location>
-    <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="gson">
-      <dependencies>
-        <dependency>
-          <groupId>com.google.code.gson</groupId>
-          <artifactId>gson</artifactId>
-          <version>2.13.1</version>
-          <type>jar</type>
-        </dependency>
-      </dependencies>
-    </location>
-    <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bytebuddy">
-      <dependencies>
-        <dependency>
-          <groupId>net.bytebuddy</groupId>
-          <artifactId>byte-buddy</artifactId>
-          <version>1.17.7</version>
-          <type>jar</type>
-        </dependency>
-        <dependency>
-          <groupId>net.bytebuddy</groupId>
-          <artifactId>byte-buddy-agent</artifactId>
-          <version>1.17.7</version>
-          <type>jar</type>
-        </dependency>
-      </dependencies>
-    </location>
-    <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bouncycastle">
-      <dependencies>
-        <dependency>
-          <groupId>org.bouncycastle</groupId>
-          <artifactId>bcpg-jdk18on</artifactId>
-          <version>1.81</version>
-          <type>jar</type>
-        </dependency>
-        <dependency>
-          <groupId>org.bouncycastle</groupId>
-          <artifactId>bcprov-jdk18on</artifactId>
-          <version>1.81</version>
-          <type>jar</type>
-        </dependency>
-        <dependency>
-          <groupId>org.bouncycastle</groupId>
-          <artifactId>bcpkix-jdk18on</artifactId>
-          <version>1.81</version>
-          <type>jar</type>
-        </dependency>
-        <dependency>
-          <groupId>org.bouncycastle</groupId>
-          <artifactId>bcutil-jdk18on</artifactId>
-          <version>1.81</version>
-          <type>jar</type>
-        </dependency>
-      </dependencies>
-    </location>
-    <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="assertj">
-      <dependencies>
-        <dependency>
-          <groupId>org.assertj</groupId>
-          <artifactId>assertj-core</artifactId>
-          <version>3.27.4</version>
-          <type>jar</type>
-        </dependency>
-      </dependencies>
-    </location>
-    <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="args4j">
-      <dependencies>
-        <dependency>
-          <groupId>args4j</groupId>
-          <artifactId>args4j</artifactId>
-          <version>2.37</version>
-          <type>jar</type>
-        </dependency>
-      </dependencies>
-    </location>
-    <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="apache">
-      <dependencies>
-        <dependency>
-          <groupId>commons-codec</groupId>
-          <artifactId>commons-codec</artifactId>
-          <version>1.19.0</version>
-          <type>jar</type>
-        </dependency>
-        <dependency>
-          <groupId>org.apache.commons</groupId>
-          <artifactId>commons-compress</artifactId>
-          <version>1.28.0</version>
-          <type>jar</type>
-        </dependency>
-        <dependency>
-          <groupId>org.apache.commons</groupId>
-          <artifactId>commons-lang3</artifactId>
-          <version>3.18.0</version>
-          <type>jar</type>
-        </dependency>
-        <dependency>
-          <groupId>commons-io</groupId>
-          <artifactId>commons-io</artifactId>
-          <version>2.20.0</version>
-          <type>jar</type>
-        </dependency>
-        <dependency>
-          <groupId>commons-logging</groupId>
-          <artifactId>commons-logging</artifactId>
-          <version>1.3.5</version>
-          <type>jar</type>
-        </dependency>
-      </dependencies>
-    </location>
-  </locations>
-</target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.33.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.33.tpd
deleted file mode 100644
index 74c6878..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.33.tpd
+++ /dev/null
@@ -1,8 +0,0 @@
-target "jgit-4.33" with source configurePhase
-
-include "orbit/orbit-4.33.tpd"
-include "maven/dependencies.tpd"
-
-location "https://download.eclipse.org/releases/2024-09/" {
-	org.eclipse.osgi lazy
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.34.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.34.target
index aa318cf..c323c45 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.34.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.34.target
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.34" sequenceNumber="1756150982">
+<target name="jgit-4.34" sequenceNumber="1761604946">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="com.jcraft.jsch" version="0.1.55.v20230916-1400"/>
@@ -10,16 +10,20 @@
       <unit id="com.jcraft.jzlib.source" version="1.1.3.v20230916-1400"/>
       <unit id="org.apache.ant" version="1.10.15.v20240901-1000"/>
       <unit id="org.apache.ant.source" version="1.10.15.v20240901-1000"/>
+      <unit id="org.apache.aries.spifly.dynamic.bundle" version="1.3.7"/>
       <unit id="org.apache.httpcomponents.httpclient" version="4.5.14"/>
       <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14"/>
       <unit id="org.apache.httpcomponents.httpcore" version="4.4.16"/>
       <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16"/>
-      <unit id="org.hamcrest.core" version="1.3.0.v20230809-1000"/>
-      <unit id="org.hamcrest.core.source" version="1.3.0.v20230809-1000"/>
-      <unit id="org.hamcrest.library" version="1.3.0.v20230809-1000"/>
-      <unit id="org.hamcrest.library.source" version="1.3.0.v20230809-1000"/>
+      <unit id="org.hamcrest" version="3.0.0"/>
+      <unit id="org.hamcrest.source" version="3.0.0"/>
       <unit id="org.junit" version="4.13.2.v20240929-1000"/>
       <unit id="org.junit.source" version="4.13.2.v20240929-1000"/>
+      <unit id="org.objectweb.asm" version="9.7.1"/>
+      <unit id="org.objectweb.asm.commons" version="9.7.1"/>
+      <unit id="org.objectweb.asm.util" version="9.7.1"/>
+      <unit id="org.objectweb.asm.tree" version="9.7.1"/>
+      <unit id="org.objectweb.asm.tree.analysis" version="9.7.1"/>
       <unit id="org.objenesis" version="3.4.0"/>
       <unit id="org.objenesis.source" version="3.4.0"/>
       <unit id="org.osgi.service.cm" version="1.6.1.202109301733"/>
@@ -45,13 +49,13 @@
         <dependency>
           <groupId>org.slf4j</groupId>
           <artifactId>slf4j-api</artifactId>
-          <version>1.7.36</version>
+          <version>2.0.17</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.slf4j</groupId>
           <artifactId>slf4j-simple</artifactId>
-          <version>1.7.36</version>
+          <version>2.0.17</version>
           <type>jar</type>
         </dependency>
       </dependencies>
@@ -77,7 +81,7 @@
         <dependency>
           <groupId>org.mockito</groupId>
           <artifactId>mockito-core</artifactId>
-          <version>5.19.0</version>
+          <version>5.20.0</version>
           <type>jar</type>
         </dependency>
       </dependencies>
@@ -87,13 +91,13 @@
         <dependency>
           <groupId>net.java.dev.jna</groupId>
           <artifactId>jna</artifactId>
-          <version>5.17.0</version>
+          <version>5.18.1</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>net.java.dev.jna</groupId>
           <artifactId>jna-platform</artifactId>
-          <version>5.17.0</version>
+          <version>5.18.1</version>
           <type>jar</type>
         </dependency>
       </dependencies>
@@ -103,49 +107,49 @@
         <dependency>
           <groupId>org.eclipse.jetty.ee10</groupId>
           <artifactId>jetty-ee10-servlet</artifactId>
-          <version>12.1.0</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-http</artifactId>
-          <version>12.1.0</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-io</artifactId>
-          <version>12.1.0</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-security</artifactId>
-          <version>12.1.0</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-server</artifactId>
-          <version>12.1.0</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-session</artifactId>
-          <version>12.1.0</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-util</artifactId>
-          <version>12.1.0</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-util-ajax</artifactId>
-          <version>12.1.0</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
@@ -171,7 +175,7 @@
         <dependency>
           <groupId>org.hamcrest</groupId>
           <artifactId>hamcrest</artifactId>
-          <version>2.2</version>
+          <version>3.0</version>
           <type>jar</type>
         </dependency>
       </dependencies>
@@ -181,7 +185,7 @@
         <dependency>
           <groupId>com.google.code.gson</groupId>
           <artifactId>gson</artifactId>
-          <version>2.13.1</version>
+          <version>2.13.2</version>
           <type>jar</type>
         </dependency>
       </dependencies>
@@ -191,13 +195,13 @@
         <dependency>
           <groupId>net.bytebuddy</groupId>
           <artifactId>byte-buddy</artifactId>
-          <version>1.17.7</version>
+          <version>1.17.8</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>net.bytebuddy</groupId>
           <artifactId>byte-buddy-agent</artifactId>
-          <version>1.17.7</version>
+          <version>1.17.8</version>
           <type>jar</type>
         </dependency>
       </dependencies>
@@ -207,25 +211,25 @@
         <dependency>
           <groupId>org.bouncycastle</groupId>
           <artifactId>bcpg-jdk18on</artifactId>
-          <version>1.81</version>
+          <version>1.82</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.bouncycastle</groupId>
           <artifactId>bcprov-jdk18on</artifactId>
-          <version>1.81</version>
+          <version>1.82</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.bouncycastle</groupId>
           <artifactId>bcpkix-jdk18on</artifactId>
-          <version>1.81</version>
+          <version>1.82</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.bouncycastle</groupId>
           <artifactId>bcutil-jdk18on</artifactId>
-          <version>1.81</version>
+          <version>1.82</version>
           <type>jar</type>
         </dependency>
       </dependencies>
@@ -235,7 +239,7 @@
         <dependency>
           <groupId>org.assertj</groupId>
           <artifactId>assertj-core</artifactId>
-          <version>3.27.4</version>
+          <version>3.27.6</version>
           <type>jar</type>
         </dependency>
       </dependencies>
@@ -267,7 +271,7 @@
         <dependency>
           <groupId>org.apache.commons</groupId>
           <artifactId>commons-lang3</artifactId>
-          <version>3.18.0</version>
+          <version>3.19.0</version>
           <type>jar</type>
         </dependency>
         <dependency>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.35.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.35.target
index 6049694..baf1e3b 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.35.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.35.target
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.35" sequenceNumber="1756150982">
+<target name="jgit-4.35" sequenceNumber="1761604946">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="com.jcraft.jsch" version="0.1.55.v20230916-1400"/>
@@ -10,16 +10,20 @@
       <unit id="com.jcraft.jzlib.source" version="1.1.3.v20230916-1400"/>
       <unit id="org.apache.ant" version="1.10.15.v20240901-1000"/>
       <unit id="org.apache.ant.source" version="1.10.15.v20240901-1000"/>
+      <unit id="org.apache.aries.spifly.dynamic.bundle" version="1.3.7"/>
       <unit id="org.apache.httpcomponents.httpclient" version="4.5.14"/>
       <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14"/>
       <unit id="org.apache.httpcomponents.httpcore" version="4.4.16"/>
       <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16"/>
-      <unit id="org.hamcrest.core" version="1.3.0.v20230809-1000"/>
-      <unit id="org.hamcrest.core.source" version="1.3.0.v20230809-1000"/>
-      <unit id="org.hamcrest.library" version="1.3.0.v20230809-1000"/>
-      <unit id="org.hamcrest.library.source" version="1.3.0.v20230809-1000"/>
+      <unit id="org.hamcrest" version="3.0.0"/>
+      <unit id="org.hamcrest.source" version="3.0.0"/>
       <unit id="org.junit" version="4.13.2.v20240929-1000"/>
       <unit id="org.junit.source" version="4.13.2.v20240929-1000"/>
+      <unit id="org.objectweb.asm" version="9.7.1"/>
+      <unit id="org.objectweb.asm.commons" version="9.7.1"/>
+      <unit id="org.objectweb.asm.util" version="9.7.1"/>
+      <unit id="org.objectweb.asm.tree" version="9.7.1"/>
+      <unit id="org.objectweb.asm.tree.analysis" version="9.7.1"/>
       <unit id="org.objenesis" version="3.4.0"/>
       <unit id="org.objenesis.source" version="3.4.0"/>
       <unit id="org.osgi.service.cm" version="1.6.1.202109301733"/>
@@ -45,13 +49,13 @@
         <dependency>
           <groupId>org.slf4j</groupId>
           <artifactId>slf4j-api</artifactId>
-          <version>1.7.36</version>
+          <version>2.0.17</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.slf4j</groupId>
           <artifactId>slf4j-simple</artifactId>
-          <version>1.7.36</version>
+          <version>2.0.17</version>
           <type>jar</type>
         </dependency>
       </dependencies>
@@ -77,7 +81,7 @@
         <dependency>
           <groupId>org.mockito</groupId>
           <artifactId>mockito-core</artifactId>
-          <version>5.19.0</version>
+          <version>5.20.0</version>
           <type>jar</type>
         </dependency>
       </dependencies>
@@ -87,13 +91,13 @@
         <dependency>
           <groupId>net.java.dev.jna</groupId>
           <artifactId>jna</artifactId>
-          <version>5.17.0</version>
+          <version>5.18.1</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>net.java.dev.jna</groupId>
           <artifactId>jna-platform</artifactId>
-          <version>5.17.0</version>
+          <version>5.18.1</version>
           <type>jar</type>
         </dependency>
       </dependencies>
@@ -103,49 +107,49 @@
         <dependency>
           <groupId>org.eclipse.jetty.ee10</groupId>
           <artifactId>jetty-ee10-servlet</artifactId>
-          <version>12.1.0</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-http</artifactId>
-          <version>12.1.0</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-io</artifactId>
-          <version>12.1.0</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-security</artifactId>
-          <version>12.1.0</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-server</artifactId>
-          <version>12.1.0</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-session</artifactId>
-          <version>12.1.0</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-util</artifactId>
-          <version>12.1.0</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-util-ajax</artifactId>
-          <version>12.1.0</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
@@ -171,7 +175,7 @@
         <dependency>
           <groupId>org.hamcrest</groupId>
           <artifactId>hamcrest</artifactId>
-          <version>2.2</version>
+          <version>3.0</version>
           <type>jar</type>
         </dependency>
       </dependencies>
@@ -181,7 +185,7 @@
         <dependency>
           <groupId>com.google.code.gson</groupId>
           <artifactId>gson</artifactId>
-          <version>2.13.1</version>
+          <version>2.13.2</version>
           <type>jar</type>
         </dependency>
       </dependencies>
@@ -191,13 +195,13 @@
         <dependency>
           <groupId>net.bytebuddy</groupId>
           <artifactId>byte-buddy</artifactId>
-          <version>1.17.7</version>
+          <version>1.17.8</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>net.bytebuddy</groupId>
           <artifactId>byte-buddy-agent</artifactId>
-          <version>1.17.7</version>
+          <version>1.17.8</version>
           <type>jar</type>
         </dependency>
       </dependencies>
@@ -207,25 +211,25 @@
         <dependency>
           <groupId>org.bouncycastle</groupId>
           <artifactId>bcpg-jdk18on</artifactId>
-          <version>1.81</version>
+          <version>1.82</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.bouncycastle</groupId>
           <artifactId>bcprov-jdk18on</artifactId>
-          <version>1.81</version>
+          <version>1.82</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.bouncycastle</groupId>
           <artifactId>bcpkix-jdk18on</artifactId>
-          <version>1.81</version>
+          <version>1.82</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.bouncycastle</groupId>
           <artifactId>bcutil-jdk18on</artifactId>
-          <version>1.81</version>
+          <version>1.82</version>
           <type>jar</type>
         </dependency>
       </dependencies>
@@ -235,7 +239,7 @@
         <dependency>
           <groupId>org.assertj</groupId>
           <artifactId>assertj-core</artifactId>
-          <version>3.27.4</version>
+          <version>3.27.6</version>
           <type>jar</type>
         </dependency>
       </dependencies>
@@ -267,7 +271,7 @@
         <dependency>
           <groupId>org.apache.commons</groupId>
           <artifactId>commons-lang3</artifactId>
-          <version>3.18.0</version>
+          <version>3.19.0</version>
           <type>jar</type>
         </dependency>
         <dependency>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.36.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.36.target
index cd2bc2b..298bc2a 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.36.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.36.target
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.36" sequenceNumber="1756150982">
+<target name="jgit-4.36" sequenceNumber="1761604946">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="com.jcraft.jsch" version="0.1.55.v20230916-1400"/>
@@ -10,16 +10,20 @@
       <unit id="com.jcraft.jzlib.source" version="1.1.3.v20230916-1400"/>
       <unit id="org.apache.ant" version="1.10.15.v20240901-1000"/>
       <unit id="org.apache.ant.source" version="1.10.15.v20240901-1000"/>
+      <unit id="org.apache.aries.spifly.dynamic.bundle" version="1.3.7"/>
       <unit id="org.apache.httpcomponents.httpclient" version="4.5.14"/>
       <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14"/>
       <unit id="org.apache.httpcomponents.httpcore" version="4.4.16"/>
       <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16"/>
-      <unit id="org.hamcrest.core" version="1.3.0.v20230809-1000"/>
-      <unit id="org.hamcrest.core.source" version="1.3.0.v20230809-1000"/>
-      <unit id="org.hamcrest.library" version="1.3.0.v20230809-1000"/>
-      <unit id="org.hamcrest.library.source" version="1.3.0.v20230809-1000"/>
+      <unit id="org.hamcrest" version="3.0.0"/>
+      <unit id="org.hamcrest.source" version="3.0.0"/>
       <unit id="org.junit" version="4.13.2.v20240929-1000"/>
       <unit id="org.junit.source" version="4.13.2.v20240929-1000"/>
+      <unit id="org.objectweb.asm" version="9.8.0"/>
+      <unit id="org.objectweb.asm.commons" version="9.8.0"/>
+      <unit id="org.objectweb.asm.util" version="9.8.0"/>
+      <unit id="org.objectweb.asm.tree" version="9.8.0"/>
+      <unit id="org.objectweb.asm.tree.analysis" version="9.8.0"/>
       <unit id="org.objenesis" version="3.4.0"/>
       <unit id="org.objenesis.source" version="3.4.0"/>
       <unit id="org.osgi.service.cm" version="1.6.1.202109301733"/>
@@ -45,13 +49,13 @@
         <dependency>
           <groupId>org.slf4j</groupId>
           <artifactId>slf4j-api</artifactId>
-          <version>1.7.36</version>
+          <version>2.0.17</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.slf4j</groupId>
           <artifactId>slf4j-simple</artifactId>
-          <version>1.7.36</version>
+          <version>2.0.17</version>
           <type>jar</type>
         </dependency>
       </dependencies>
@@ -77,7 +81,7 @@
         <dependency>
           <groupId>org.mockito</groupId>
           <artifactId>mockito-core</artifactId>
-          <version>5.19.0</version>
+          <version>5.20.0</version>
           <type>jar</type>
         </dependency>
       </dependencies>
@@ -87,13 +91,13 @@
         <dependency>
           <groupId>net.java.dev.jna</groupId>
           <artifactId>jna</artifactId>
-          <version>5.17.0</version>
+          <version>5.18.1</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>net.java.dev.jna</groupId>
           <artifactId>jna-platform</artifactId>
-          <version>5.17.0</version>
+          <version>5.18.1</version>
           <type>jar</type>
         </dependency>
       </dependencies>
@@ -103,49 +107,49 @@
         <dependency>
           <groupId>org.eclipse.jetty.ee10</groupId>
           <artifactId>jetty-ee10-servlet</artifactId>
-          <version>12.1.0</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-http</artifactId>
-          <version>12.1.0</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-io</artifactId>
-          <version>12.1.0</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-security</artifactId>
-          <version>12.1.0</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-server</artifactId>
-          <version>12.1.0</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-session</artifactId>
-          <version>12.1.0</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-util</artifactId>
-          <version>12.1.0</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-util-ajax</artifactId>
-          <version>12.1.0</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
@@ -171,7 +175,7 @@
         <dependency>
           <groupId>org.hamcrest</groupId>
           <artifactId>hamcrest</artifactId>
-          <version>2.2</version>
+          <version>3.0</version>
           <type>jar</type>
         </dependency>
       </dependencies>
@@ -181,7 +185,7 @@
         <dependency>
           <groupId>com.google.code.gson</groupId>
           <artifactId>gson</artifactId>
-          <version>2.13.1</version>
+          <version>2.13.2</version>
           <type>jar</type>
         </dependency>
       </dependencies>
@@ -191,13 +195,13 @@
         <dependency>
           <groupId>net.bytebuddy</groupId>
           <artifactId>byte-buddy</artifactId>
-          <version>1.17.7</version>
+          <version>1.17.8</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>net.bytebuddy</groupId>
           <artifactId>byte-buddy-agent</artifactId>
-          <version>1.17.7</version>
+          <version>1.17.8</version>
           <type>jar</type>
         </dependency>
       </dependencies>
@@ -207,25 +211,25 @@
         <dependency>
           <groupId>org.bouncycastle</groupId>
           <artifactId>bcpg-jdk18on</artifactId>
-          <version>1.81</version>
+          <version>1.82</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.bouncycastle</groupId>
           <artifactId>bcprov-jdk18on</artifactId>
-          <version>1.81</version>
+          <version>1.82</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.bouncycastle</groupId>
           <artifactId>bcpkix-jdk18on</artifactId>
-          <version>1.81</version>
+          <version>1.82</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.bouncycastle</groupId>
           <artifactId>bcutil-jdk18on</artifactId>
-          <version>1.81</version>
+          <version>1.82</version>
           <type>jar</type>
         </dependency>
       </dependencies>
@@ -235,7 +239,7 @@
         <dependency>
           <groupId>org.assertj</groupId>
           <artifactId>assertj-core</artifactId>
-          <version>3.27.4</version>
+          <version>3.27.6</version>
           <type>jar</type>
         </dependency>
       </dependencies>
@@ -267,7 +271,7 @@
         <dependency>
           <groupId>org.apache.commons</groupId>
           <artifactId>commons-lang3</artifactId>
-          <version>3.18.0</version>
+          <version>3.19.0</version>
           <type>jar</type>
         </dependency>
         <dependency>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.37.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.37.target
index 54bccdd..ec93cd0 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.37.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.37.target
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.37" sequenceNumber="1756150985">
+<target name="jgit-4.37" sequenceNumber="1761604946">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="com.jcraft.jsch" version="0.1.55.v20230916-1400"/>
@@ -10,16 +10,20 @@
       <unit id="com.jcraft.jzlib.source" version="1.1.3.v20230916-1400"/>
       <unit id="org.apache.ant" version="1.10.15.v20240901-1000"/>
       <unit id="org.apache.ant.source" version="1.10.15.v20240901-1000"/>
+      <unit id="org.apache.aries.spifly.dynamic.bundle" version="1.3.7"/>
       <unit id="org.apache.httpcomponents.httpclient" version="4.5.14"/>
       <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14"/>
       <unit id="org.apache.httpcomponents.httpcore" version="4.4.16"/>
       <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16"/>
-      <unit id="org.hamcrest.core" version="1.3.0.v20230809-1000"/>
-      <unit id="org.hamcrest.core.source" version="1.3.0.v20230809-1000"/>
-      <unit id="org.hamcrest.library" version="1.3.0.v20230809-1000"/>
-      <unit id="org.hamcrest.library.source" version="1.3.0.v20230809-1000"/>
+      <unit id="org.hamcrest" version="3.0.0"/>
+      <unit id="org.hamcrest.source" version="3.0.0"/>
       <unit id="org.junit" version="4.13.2.v20240929-1000"/>
       <unit id="org.junit.source" version="4.13.2.v20240929-1000"/>
+      <unit id="org.objectweb.asm" version="9.8.0"/>
+      <unit id="org.objectweb.asm.commons" version="9.8.0"/>
+      <unit id="org.objectweb.asm.util" version="9.8.0"/>
+      <unit id="org.objectweb.asm.tree" version="9.8.0"/>
+      <unit id="org.objectweb.asm.tree.analysis" version="9.8.0"/>
       <unit id="org.objenesis" version="3.4.0"/>
       <unit id="org.objenesis.source" version="3.4.0"/>
       <unit id="org.osgi.service.cm" version="1.6.1.202109301733"/>
@@ -45,13 +49,13 @@
         <dependency>
           <groupId>org.slf4j</groupId>
           <artifactId>slf4j-api</artifactId>
-          <version>1.7.36</version>
+          <version>2.0.17</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.slf4j</groupId>
           <artifactId>slf4j-simple</artifactId>
-          <version>1.7.36</version>
+          <version>2.0.17</version>
           <type>jar</type>
         </dependency>
       </dependencies>
@@ -77,7 +81,7 @@
         <dependency>
           <groupId>org.mockito</groupId>
           <artifactId>mockito-core</artifactId>
-          <version>5.19.0</version>
+          <version>5.20.0</version>
           <type>jar</type>
         </dependency>
       </dependencies>
@@ -87,13 +91,13 @@
         <dependency>
           <groupId>net.java.dev.jna</groupId>
           <artifactId>jna</artifactId>
-          <version>5.17.0</version>
+          <version>5.18.1</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>net.java.dev.jna</groupId>
           <artifactId>jna-platform</artifactId>
-          <version>5.17.0</version>
+          <version>5.18.1</version>
           <type>jar</type>
         </dependency>
       </dependencies>
@@ -103,49 +107,49 @@
         <dependency>
           <groupId>org.eclipse.jetty.ee10</groupId>
           <artifactId>jetty-ee10-servlet</artifactId>
-          <version>12.1.0</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-http</artifactId>
-          <version>12.1.0</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-io</artifactId>
-          <version>12.1.0</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-security</artifactId>
-          <version>12.1.0</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-server</artifactId>
-          <version>12.1.0</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-session</artifactId>
-          <version>12.1.0</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-util</artifactId>
-          <version>12.1.0</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-util-ajax</artifactId>
-          <version>12.1.0</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
@@ -171,7 +175,7 @@
         <dependency>
           <groupId>org.hamcrest</groupId>
           <artifactId>hamcrest</artifactId>
-          <version>2.2</version>
+          <version>3.0</version>
           <type>jar</type>
         </dependency>
       </dependencies>
@@ -181,7 +185,7 @@
         <dependency>
           <groupId>com.google.code.gson</groupId>
           <artifactId>gson</artifactId>
-          <version>2.13.1</version>
+          <version>2.13.2</version>
           <type>jar</type>
         </dependency>
       </dependencies>
@@ -191,13 +195,13 @@
         <dependency>
           <groupId>net.bytebuddy</groupId>
           <artifactId>byte-buddy</artifactId>
-          <version>1.17.7</version>
+          <version>1.17.8</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>net.bytebuddy</groupId>
           <artifactId>byte-buddy-agent</artifactId>
-          <version>1.17.7</version>
+          <version>1.17.8</version>
           <type>jar</type>
         </dependency>
       </dependencies>
@@ -207,25 +211,25 @@
         <dependency>
           <groupId>org.bouncycastle</groupId>
           <artifactId>bcpg-jdk18on</artifactId>
-          <version>1.81</version>
+          <version>1.82</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.bouncycastle</groupId>
           <artifactId>bcprov-jdk18on</artifactId>
-          <version>1.81</version>
+          <version>1.82</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.bouncycastle</groupId>
           <artifactId>bcpkix-jdk18on</artifactId>
-          <version>1.81</version>
+          <version>1.82</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.bouncycastle</groupId>
           <artifactId>bcutil-jdk18on</artifactId>
-          <version>1.81</version>
+          <version>1.82</version>
           <type>jar</type>
         </dependency>
       </dependencies>
@@ -235,7 +239,7 @@
         <dependency>
           <groupId>org.assertj</groupId>
           <artifactId>assertj-core</artifactId>
-          <version>3.27.4</version>
+          <version>3.27.6</version>
           <type>jar</type>
         </dependency>
       </dependencies>
@@ -267,7 +271,7 @@
         <dependency>
           <groupId>org.apache.commons</groupId>
           <artifactId>commons-lang3</artifactId>
-          <version>3.18.0</version>
+          <version>3.19.0</version>
           <type>jar</type>
         </dependency>
         <dependency>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.32.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.38.target
similarity index 85%
rename from org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.32.target
rename to org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.38.target
index 4765602..c0f3dd9 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.32.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.38.target
@@ -1,34 +1,38 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.32" sequenceNumber="1756150982">
+<target name="jgit-4.38" sequenceNumber="1761604947">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="com.jcraft.jsch" version="0.1.55.v20230916-1400"/>
       <unit id="com.jcraft.jsch.source" version="0.1.55.v20230916-1400"/>
       <unit id="com.jcraft.jzlib" version="1.1.3.v20230916-1400"/>
       <unit id="com.jcraft.jzlib.source" version="1.1.3.v20230916-1400"/>
-      <unit id="org.apache.ant" version="1.10.14.v20230922-1200"/>
-      <unit id="org.apache.ant.source" version="1.10.14.v20230922-1200"/>
+      <unit id="org.apache.ant" version="1.10.15.v20240901-1000"/>
+      <unit id="org.apache.ant.source" version="1.10.15.v20240901-1000"/>
+      <unit id="org.apache.aries.spifly.dynamic.bundle" version="1.3.7"/>
       <unit id="org.apache.httpcomponents.httpclient" version="4.5.14"/>
       <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14"/>
       <unit id="org.apache.httpcomponents.httpcore" version="4.4.16"/>
       <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16"/>
-      <unit id="org.hamcrest.core" version="1.3.0.v20230809-1000"/>
-      <unit id="org.hamcrest.core.source" version="1.3.0.v20230809-1000"/>
-      <unit id="org.hamcrest.library" version="1.3.0.v20230809-1000"/>
-      <unit id="org.hamcrest.library.source" version="1.3.0.v20230809-1000"/>
-      <unit id="org.junit" version="4.13.2.v20230809-1000"/>
-      <unit id="org.junit.source" version="4.13.2.v20230809-1000"/>
+      <unit id="org.hamcrest" version="3.0.0"/>
+      <unit id="org.hamcrest.source" version="3.0.0"/>
+      <unit id="org.junit" version="4.13.2.v20240929-1000"/>
+      <unit id="org.junit.source" version="4.13.2.v20240929-1000"/>
+      <unit id="org.objectweb.asm" version="9.9.0"/>
+      <unit id="org.objectweb.asm.commons" version="9.9.0"/>
+      <unit id="org.objectweb.asm.util" version="9.9.0"/>
+      <unit id="org.objectweb.asm.tree" version="9.9.0"/>
+      <unit id="org.objectweb.asm.tree.analysis" version="9.9.0"/>
       <unit id="org.objenesis" version="3.4.0"/>
       <unit id="org.objenesis.source" version="3.4.0"/>
       <unit id="org.osgi.service.cm" version="1.6.1.202109301733"/>
       <unit id="org.osgi.service.cm.source" version="1.6.1.202109301733"/>
-      <repository location="https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2024-06"/>
+      <repository location="https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2025-12"/>
     </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/2024-06/"/>
+      <repository location="https://download.eclipse.org/staging/2025-12/"/>
     </location>
     <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="xz">
       <dependencies>
@@ -45,13 +49,13 @@
         <dependency>
           <groupId>org.slf4j</groupId>
           <artifactId>slf4j-api</artifactId>
-          <version>1.7.36</version>
+          <version>2.0.17</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.slf4j</groupId>
           <artifactId>slf4j-simple</artifactId>
-          <version>1.7.36</version>
+          <version>2.0.17</version>
           <type>jar</type>
         </dependency>
       </dependencies>
@@ -77,7 +81,7 @@
         <dependency>
           <groupId>org.mockito</groupId>
           <artifactId>mockito-core</artifactId>
-          <version>5.19.0</version>
+          <version>5.20.0</version>
           <type>jar</type>
         </dependency>
       </dependencies>
@@ -87,13 +91,13 @@
         <dependency>
           <groupId>net.java.dev.jna</groupId>
           <artifactId>jna</artifactId>
-          <version>5.17.0</version>
+          <version>5.18.1</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>net.java.dev.jna</groupId>
           <artifactId>jna-platform</artifactId>
-          <version>5.17.0</version>
+          <version>5.18.1</version>
           <type>jar</type>
         </dependency>
       </dependencies>
@@ -103,49 +107,49 @@
         <dependency>
           <groupId>org.eclipse.jetty.ee10</groupId>
           <artifactId>jetty-ee10-servlet</artifactId>
-          <version>12.1.0</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-http</artifactId>
-          <version>12.1.0</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-io</artifactId>
-          <version>12.1.0</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-security</artifactId>
-          <version>12.1.0</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-server</artifactId>
-          <version>12.1.0</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-session</artifactId>
-          <version>12.1.0</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-util</artifactId>
-          <version>12.1.0</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-util-ajax</artifactId>
-          <version>12.1.0</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
@@ -171,7 +175,7 @@
         <dependency>
           <groupId>org.hamcrest</groupId>
           <artifactId>hamcrest</artifactId>
-          <version>2.2</version>
+          <version>3.0</version>
           <type>jar</type>
         </dependency>
       </dependencies>
@@ -181,7 +185,7 @@
         <dependency>
           <groupId>com.google.code.gson</groupId>
           <artifactId>gson</artifactId>
-          <version>2.13.1</version>
+          <version>2.13.2</version>
           <type>jar</type>
         </dependency>
       </dependencies>
@@ -191,13 +195,13 @@
         <dependency>
           <groupId>net.bytebuddy</groupId>
           <artifactId>byte-buddy</artifactId>
-          <version>1.17.7</version>
+          <version>1.17.8</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>net.bytebuddy</groupId>
           <artifactId>byte-buddy-agent</artifactId>
-          <version>1.17.7</version>
+          <version>1.17.8</version>
           <type>jar</type>
         </dependency>
       </dependencies>
@@ -207,25 +211,25 @@
         <dependency>
           <groupId>org.bouncycastle</groupId>
           <artifactId>bcpg-jdk18on</artifactId>
-          <version>1.81</version>
+          <version>1.82</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.bouncycastle</groupId>
           <artifactId>bcprov-jdk18on</artifactId>
-          <version>1.81</version>
+          <version>1.82</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.bouncycastle</groupId>
           <artifactId>bcpkix-jdk18on</artifactId>
-          <version>1.81</version>
+          <version>1.82</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.bouncycastle</groupId>
           <artifactId>bcutil-jdk18on</artifactId>
-          <version>1.81</version>
+          <version>1.82</version>
           <type>jar</type>
         </dependency>
       </dependencies>
@@ -235,7 +239,7 @@
         <dependency>
           <groupId>org.assertj</groupId>
           <artifactId>assertj-core</artifactId>
-          <version>3.27.4</version>
+          <version>3.27.6</version>
           <type>jar</type>
         </dependency>
       </dependencies>
@@ -267,7 +271,7 @@
         <dependency>
           <groupId>org.apache.commons</groupId>
           <artifactId>commons-lang3</artifactId>
-          <version>3.18.0</version>
+          <version>3.19.0</version>
           <type>jar</type>
         </dependency>
         <dependency>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.38.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.38.tpd
new file mode 100644
index 0000000..cd20f43
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.38.tpd
@@ -0,0 +1,8 @@
+target "jgit-4.38" with source configurePhase
+
+include "orbit/orbit-4.38.tpd"
+include "maven/dependencies.tpd"
+
+location "https://download.eclipse.org/staging/2025-12/" {
+	org.eclipse.osgi lazy
+}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/maven/dependencies.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/maven/dependencies.tpd
index f901454..5e4f998 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/maven/dependencies.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/maven/dependencies.tpd
@@ -20,7 +20,7 @@
 	dependency {
 		groupId = "org.apache.commons"
 		artifactId = "commons-lang3"
-		version = "3.18.0"
+		version = "3.19.0"
 	}
 	dependency {
 		groupId = "commons-io"
@@ -56,7 +56,7 @@
 	dependency {
 		groupId = "org.assertj"
 		artifactId = "assertj-core"
-		version = "3.27.4"
+		version = "3.27.6"
 	}
 }
 
@@ -69,22 +69,22 @@
 	dependency {
 		groupId = "org.bouncycastle"
 		artifactId = "bcpg-jdk18on"
-		version = "1.81"
+		version = "1.82"
 	}
 	dependency {
 		groupId = "org.bouncycastle"
 		artifactId = "bcprov-jdk18on"
-		version = "1.81"
+		version = "1.82"
 	}
 	dependency {
 		groupId = "org.bouncycastle"
 		artifactId = "bcpkix-jdk18on"
-		version = "1.81"
+		version = "1.82"
 	}
 	dependency {
 		groupId = "org.bouncycastle"
 		artifactId = "bcutil-jdk18on"
-		version = "1.81"
+		version = "1.82"
 	}
 }
 
@@ -97,12 +97,12 @@
 	dependency {
 		groupId = "net.bytebuddy"
 		artifactId = "byte-buddy"
-		version = "1.17.7"
+		version = "1.17.8"
 	}
 	dependency {
 		groupId = "net.bytebuddy"
 		artifactId = "byte-buddy-agent"
-		version = "1.17.7"
+		version = "1.17.8"
 	}
 }
 
@@ -115,7 +115,7 @@
 	dependency {
 		groupId = "com.google.code.gson"
 		artifactId = "gson"
-		version = "2.13.1"
+		version = "2.13.2"
 	}
 }
 
@@ -128,7 +128,7 @@
 	dependency {
 		groupId = "org.hamcrest"
 		artifactId = "hamcrest"
-		version = "2.2"
+		version = "3.0"
 	}
 }
 
@@ -154,42 +154,42 @@
 	dependency {
 		groupId = "org.eclipse.jetty.ee10"
 		artifactId = "jetty-ee10-servlet"
-		version = "12.1.0"
+		version = "12.1.3"
 	}
 	dependency {
 		groupId = "org.eclipse.jetty"
 		artifactId = "jetty-http"
-		version = "12.1.0"
+		version = "12.1.3"
 	}
 	dependency {
 		groupId = "org.eclipse.jetty"
 		artifactId = "jetty-io"
-		version = "12.1.0"
+		version = "12.1.3"
 	}
 	dependency {
 		groupId = "org.eclipse.jetty"
 		artifactId = "jetty-security"
-		version = "12.1.0"
+		version = "12.1.3"
 	}
 	dependency {
 		groupId = "org.eclipse.jetty"
 		artifactId = "jetty-server"
-		version = "12.1.0"
+		version = "12.1.3"
 	}
 	dependency {
 		groupId = "org.eclipse.jetty"
 		artifactId = "jetty-session"
-		version = "12.1.0"
+		version = "12.1.3"
 	}
 	dependency {
 		groupId = "org.eclipse.jetty"
 		artifactId = "jetty-util"
-		version = "12.1.0"
+		version = "12.1.3"
 	}
 	dependency {
 		groupId = "org.eclipse.jetty"
 		artifactId = "jetty-util-ajax"
-		version = "12.1.0"
+		version = "12.1.3"
 	}
 	dependency {
 		groupId = "jakarta.servlet"
@@ -207,12 +207,12 @@
 	dependency {
 		groupId = "net.java.dev.jna"
 		artifactId = "jna"
-		version = "5.17.0"
+		version = "5.18.1"
 	}
 	dependency {
 		groupId = "net.java.dev.jna"
 		artifactId = "jna-platform"
-		version = "5.17.0"
+		version = "5.18.1"
 	}
 }
 
@@ -225,7 +225,7 @@
 	dependency {
 		groupId = "org.mockito"
 		artifactId = "mockito-core"
-		version = "5.19.0"
+		version = "5.20.0"
 	}
 }
 
@@ -256,12 +256,12 @@
 	dependency {
 		groupId = "org.slf4j"
 		artifactId = "slf4j-api"
-		version = "1.7.36"
+		version = "2.0.17"
 	}
 	dependency {
 		groupId = "org.slf4j"
 		artifactId = "slf4j-simple"
-		version = "1.7.36"
+		version = "2.0.17"
 	}
 }
 
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.32.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.32.tpd
deleted file mode 100644
index 59fcd87..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.32.tpd
+++ /dev/null
@@ -1,25 +0,0 @@
-target "orbit-4.32" with source configurePhase
-// see https://download.eclipse.org/tools/orbit/downloads/
-
-location "https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2024-06" {
-	com.jcraft.jsch [0.1.55.v20230916-1400,0.1.55.v20230916-1400]
-	com.jcraft.jsch.source [0.1.55.v20230916-1400,0.1.55.v20230916-1400]
-	com.jcraft.jzlib [1.1.3.v20230916-1400,1.1.3.v20230916-1400]
-	com.jcraft.jzlib.source [1.1.3.v20230916-1400,1.1.3.v20230916-1400]
-	org.apache.ant [1.10.14.v20230922-1200,1.10.14.v20230922-1200]
-	org.apache.ant.source [1.10.14.v20230922-1200,1.10.14.v20230922-1200]
-	org.apache.httpcomponents.httpclient [4.5.14,4.5.14]
-	org.apache.httpcomponents.httpclient.source [4.5.14,4.5.14]
-	org.apache.httpcomponents.httpcore [4.4.16,4.4.16]
-	org.apache.httpcomponents.httpcore.source [4.4.16,4.4.16]
-	org.hamcrest.core [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
-	org.hamcrest.core.source [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
-	org.hamcrest.library [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
-	org.hamcrest.library.source [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
-	org.junit [4.13.2.v20230809-1000,4.13.2.v20230809-1000]
-	org.junit.source [4.13.2.v20230809-1000,4.13.2.v20230809-1000]
-	org.objenesis [3.4,3.4]
-	org.objenesis.source [3.4,3.4]
-	org.osgi.service.cm [1.6.1.202109301733,1.6.1.202109301733]
-	org.osgi.service.cm.source [1.6.1.202109301733,1.6.1.202109301733]
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.33.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.33.tpd
deleted file mode 100644
index 2cfa0a8..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.33.tpd
+++ /dev/null
@@ -1,25 +0,0 @@
-target "orbit-4.33" with source configurePhase
-// see https://download.eclipse.org/tools/orbit/downloads/
-
-location "https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2024-09" {
-	com.jcraft.jsch [0.1.55.v20230916-1400,0.1.55.v20230916-1400]
-	com.jcraft.jsch.source [0.1.55.v20230916-1400,0.1.55.v20230916-1400]
-	com.jcraft.jzlib [1.1.3.v20230916-1400,1.1.3.v20230916-1400]
-	com.jcraft.jzlib.source [1.1.3.v20230916-1400,1.1.3.v20230916-1400]
-	org.apache.ant [1.10.14.v20230922-1200,1.10.14.v20230922-1200]
-	org.apache.ant.source [1.10.14.v20230922-1200,1.10.14.v20230922-1200]
-	org.apache.httpcomponents.httpclient [4.5.14,4.5.14]
-	org.apache.httpcomponents.httpclient.source [4.5.14,4.5.14]
-	org.apache.httpcomponents.httpcore [4.4.16,4.4.16]
-	org.apache.httpcomponents.httpcore.source [4.4.16,4.4.16]
-	org.hamcrest.core [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
-	org.hamcrest.core.source [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
-	org.hamcrest.library [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
-	org.hamcrest.library.source [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
-	org.junit [4.13.2.v20230809-1000,4.13.2.v20230809-1000]
-	org.junit.source [4.13.2.v20230809-1000,4.13.2.v20230809-1000]
-	org.objenesis [3.4,3.4]
-	org.objenesis.source [3.4,3.4]
-	org.osgi.service.cm [1.6.1.202109301733,1.6.1.202109301733]
-	org.osgi.service.cm.source [1.6.1.202109301733,1.6.1.202109301733]
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.34.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.34.tpd
index d3e15bb..d3c2f2f 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.34.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.34.tpd
@@ -8,16 +8,20 @@
 	com.jcraft.jzlib.source [1.1.3.v20230916-1400,1.1.3.v20230916-1400]
 	org.apache.ant [1.10.15.v20240901-1000,1.10.15.v20240901-1000]
 	org.apache.ant.source [1.10.15.v20240901-1000,1.10.15.v20240901-1000]
+	org.apache.aries.spifly.dynamic.bundle [1.3.7,1.3.7]
 	org.apache.httpcomponents.httpclient [4.5.14,4.5.14]
 	org.apache.httpcomponents.httpclient.source [4.5.14,4.5.14]
 	org.apache.httpcomponents.httpcore [4.4.16,4.4.16]
 	org.apache.httpcomponents.httpcore.source [4.4.16,4.4.16]
-	org.hamcrest.core [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
-	org.hamcrest.core.source [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
-	org.hamcrest.library [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
-	org.hamcrest.library.source [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
+	org.hamcrest [3.0.0,3.0.0]
+	org.hamcrest.source [3.0.0,3.0.0]
 	org.junit [4.13.2.v20240929-1000,4.13.2.v20240929-1000]
 	org.junit.source [4.13.2.v20240929-1000,4.13.2.v20240929-1000]
+	org.objectweb.asm [9.7.1,9.7.1]
+	org.objectweb.asm.commons [9.7.1,9.7.1]
+	org.objectweb.asm.util [9.7.1,9.7.1]
+	org.objectweb.asm.tree [9.7.1,9.7.1]
+	org.objectweb.asm.tree.analysis [9.7.1,9.7.1]
 	org.objenesis [3.4,3.4]
 	org.objenesis.source [3.4,3.4]
 	org.osgi.service.cm [1.6.1.202109301733,1.6.1.202109301733]
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.35.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.35.tpd
index ec6996e..f5f9a08 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.35.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.35.tpd
@@ -8,16 +8,20 @@
 	com.jcraft.jzlib.source [1.1.3.v20230916-1400,1.1.3.v20230916-1400]
 	org.apache.ant [1.10.15.v20240901-1000,1.10.15.v20240901-1000]
 	org.apache.ant.source [1.10.15.v20240901-1000,1.10.15.v20240901-1000]
+	org.apache.aries.spifly.dynamic.bundle [1.3.7,1.3.7]
 	org.apache.httpcomponents.httpclient [4.5.14,4.5.14]
 	org.apache.httpcomponents.httpclient.source [4.5.14,4.5.14]
 	org.apache.httpcomponents.httpcore [4.4.16,4.4.16]
 	org.apache.httpcomponents.httpcore.source [4.4.16,4.4.16]
-	org.hamcrest.core [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
-	org.hamcrest.core.source [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
-	org.hamcrest.library [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
-	org.hamcrest.library.source [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
+	org.hamcrest [3.0.0,3.0.0]
+	org.hamcrest.source [3.0.0,3.0.0]
 	org.junit [4.13.2.v20240929-1000,4.13.2.v20240929-1000]
 	org.junit.source [4.13.2.v20240929-1000,4.13.2.v20240929-1000]
+	org.objectweb.asm [9.7.1,9.7.1]
+	org.objectweb.asm.commons [9.7.1,9.7.1]
+	org.objectweb.asm.util [9.7.1,9.7.1]
+	org.objectweb.asm.tree [9.7.1,9.7.1]
+	org.objectweb.asm.tree.analysis [9.7.1,9.7.1]
 	org.objenesis [3.4,3.4]
 	org.objenesis.source [3.4,3.4]
 	org.osgi.service.cm [1.6.1.202109301733,1.6.1.202109301733]
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.36.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.36.tpd
index 4f46583..b60e90d 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.36.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.36.tpd
@@ -8,16 +8,20 @@
 	com.jcraft.jzlib.source [1.1.3.v20230916-1400,1.1.3.v20230916-1400]
 	org.apache.ant [1.10.15.v20240901-1000,1.10.15.v20240901-1000]
 	org.apache.ant.source [1.10.15.v20240901-1000,1.10.15.v20240901-1000]
+	org.apache.aries.spifly.dynamic.bundle [1.3.7,1.3.7]
 	org.apache.httpcomponents.httpclient [4.5.14,4.5.14]
 	org.apache.httpcomponents.httpclient.source [4.5.14,4.5.14]
 	org.apache.httpcomponents.httpcore [4.4.16,4.4.16]
 	org.apache.httpcomponents.httpcore.source [4.4.16,4.4.16]
-	org.hamcrest.core [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
-	org.hamcrest.core.source [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
-	org.hamcrest.library [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
-	org.hamcrest.library.source [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
+	org.hamcrest [3.0.0,3.0.0]
+	org.hamcrest.source [3.0.0,3.0.0]
 	org.junit [4.13.2.v20240929-1000,4.13.2.v20240929-1000]
 	org.junit.source [4.13.2.v20240929-1000,4.13.2.v20240929-1000]
+	org.objectweb.asm [9.8.0,9.8.0]
+	org.objectweb.asm.commons [9.8.0,9.8.0]
+	org.objectweb.asm.util [9.8.0,9.8.0]
+	org.objectweb.asm.tree [9.8.0,9.8.0]
+	org.objectweb.asm.tree.analysis [9.8.0,9.8.0]
 	org.objenesis [3.4,3.4]
 	org.objenesis.source [3.4,3.4]
 	org.osgi.service.cm [1.6.1.202109301733,1.6.1.202109301733]
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.37.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.37.tpd
index 9c1ba4f..c142490 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.37.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.37.tpd
@@ -8,16 +8,20 @@
 	com.jcraft.jzlib.source [1.1.3.v20230916-1400,1.1.3.v20230916-1400]
 	org.apache.ant [1.10.15.v20240901-1000,1.10.15.v20240901-1000]
 	org.apache.ant.source [1.10.15.v20240901-1000,1.10.15.v20240901-1000]
+	org.apache.aries.spifly.dynamic.bundle [1.3.7,1.3.7]
 	org.apache.httpcomponents.httpclient [4.5.14,4.5.14]
 	org.apache.httpcomponents.httpclient.source [4.5.14,4.5.14]
 	org.apache.httpcomponents.httpcore [4.4.16,4.4.16]
 	org.apache.httpcomponents.httpcore.source [4.4.16,4.4.16]
-	org.hamcrest.core [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
-	org.hamcrest.core.source [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
-	org.hamcrest.library [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
-	org.hamcrest.library.source [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
+	org.hamcrest [3.0.0,3.0.0]
+	org.hamcrest.source [3.0.0,3.0.0]
 	org.junit [4.13.2.v20240929-1000,4.13.2.v20240929-1000]
 	org.junit.source [4.13.2.v20240929-1000,4.13.2.v20240929-1000]
+	org.objectweb.asm [9.8.0,9.8.0]
+	org.objectweb.asm.commons [9.8.0,9.8.0]
+	org.objectweb.asm.util [9.8.0,9.8.0]
+	org.objectweb.asm.tree [9.8.0,9.8.0]
+	org.objectweb.asm.tree.analysis [9.8.0,9.8.0]
 	org.objenesis [3.4,3.4]
 	org.objenesis.source [3.4,3.4]
 	org.osgi.service.cm [1.6.1.202109301733,1.6.1.202109301733]
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.38.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.38.tpd
new file mode 100644
index 0000000..7601cc4
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.38.tpd
@@ -0,0 +1,29 @@
+target "orbit-4.38" with source configurePhase
+// see https://download.eclipse.org/tools/orbit/downloads/
+
+location "https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2025-12" {
+	com.jcraft.jsch [0.1.55.v20230916-1400,0.1.55.v20230916-1400]
+	com.jcraft.jsch.source [0.1.55.v20230916-1400,0.1.55.v20230916-1400]
+	com.jcraft.jzlib [1.1.3.v20230916-1400,1.1.3.v20230916-1400]
+	com.jcraft.jzlib.source [1.1.3.v20230916-1400,1.1.3.v20230916-1400]
+	org.apache.ant [1.10.15.v20240901-1000,1.10.15.v20240901-1000]
+	org.apache.ant.source [1.10.15.v20240901-1000,1.10.15.v20240901-1000]
+	org.apache.aries.spifly.dynamic.bundle [1.3.7,1.3.7]
+	org.apache.httpcomponents.httpclient [4.5.14,4.5.14]
+	org.apache.httpcomponents.httpclient.source [4.5.14,4.5.14]
+	org.apache.httpcomponents.httpcore [4.4.16,4.4.16]
+	org.apache.httpcomponents.httpcore.source [4.4.16,4.4.16]
+	org.hamcrest [3.0.0,3.0.0]
+	org.hamcrest.source [3.0.0,3.0.0]
+	org.junit [4.13.2.v20240929-1000,4.13.2.v20240929-1000]
+	org.junit.source [4.13.2.v20240929-1000,4.13.2.v20240929-1000]
+	org.objectweb.asm [9.9.0,9.9.0]
+	org.objectweb.asm.commons [9.9.0,9.9.0]
+	org.objectweb.asm.util [9.9.0,9.9.0]
+	org.objectweb.asm.tree [9.9.0,9.9.0]
+	org.objectweb.asm.tree.analysis [9.9.0,9.9.0]
+	org.objenesis [3.4,3.4]
+	org.objenesis.source [3.4,3.4]
+	org.osgi.service.cm [1.6.1.202109301733,1.6.1.202109301733]
+	org.osgi.service.cm.source [1.6.1.202109301733,1.6.1.202109301733]
+}
diff --git a/org.eclipse.jgit.packaging/pom.xml b/org.eclipse.jgit.packaging/pom.xml
index 1d0eff4..301ed91 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>7.4.1-SNAPSHOT</version>
+  <version>7.5.0-SNAPSHOT</version>
   <packaging>pom</packaging>
 
   <name>JGit Tycho Parent</name>
@@ -31,7 +31,7 @@
   <properties>
     <java.version>17</java.version>
     <tycho-version>4.0.13</tycho-version>
-    <target-platform>jgit-4.32</target-platform>
+    <target-platform>jgit-4.34</target-platform>
     <project.build.outputTimestamp>${git.commit.time}</project.build.outputTimestamp>
   </properties>
 
diff --git a/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF
index d7b85af..6b4b3cd 100644
--- a/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF
@@ -3,31 +3,31 @@
 Bundle-Name: %Bundle-Name
 Automatic-Module-Name: org.eclipse.jgit.pgm.test
 Bundle-SymbolicName: org.eclipse.jgit.pgm.test
-Bundle-Version: 7.4.1.qualifier
+Bundle-Version: 7.5.0.qualifier
 Bundle-Vendor: %Bundle-Vendor
 Bundle-Localization: plugin
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: JavaSE-17
 Bundle-SCM: url=https://github.com/eclipse-jgit/jgit, connection=scm:git:https://eclipse.gerrithub.io/eclipse-jgit/jgit.git, developerConnection=scm:git:https://eclipse.gerrithub.io/a/eclipse-jgit/jgit.git
-Import-Package: org.eclipse.jgit.api;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.api.errors;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.diff;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.dircache;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal.diffmergetool;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal.storage.file;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.junit;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.lib;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.lib.internal;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.merge;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.pgm;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.pgm.internal;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.pgm.opt;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.revwalk;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.storage.file;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.transport;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.treewalk;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.util;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.util.io;version="[7.4.1,7.5.0)",
+Import-Package: org.eclipse.jgit.api;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.api.errors;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.diff;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.dircache;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal.diffmergetool;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal.storage.file;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.junit;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.lib;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.lib.internal;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.merge;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.pgm;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.pgm.internal;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.pgm.opt;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.revwalk;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.storage.file;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.transport;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.treewalk;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.util;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.util.io;version="[7.5.0,7.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 a389ab7..b8aa902 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>7.4.1-SNAPSHOT</version>
+    <version>7.5.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.pgm.test</artifactId>
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CheckoutTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CheckoutTest.java
index d533829..59d772d 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CheckoutTest.java
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CheckoutTest.java
@@ -97,9 +97,7 @@ public void testCheckoutNonExistingBranch() throws Exception {
 
 	@Test
 	public void testCheckoutWithNoRef() throws Exception {
-		assertStringArrayEquals(
-				"a valid ref is expected",
-				executeExpectingException("git checkout"));
+		assertStringArrayEquals("", execute("git checkout"));
 	}
 
 	@Test
diff --git a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
index 1e03676..70e8fed 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: 7.4.1.qualifier
+Bundle-Version: 7.5.0.qualifier
 Bundle-Vendor: %Bundle-Vendor
 Bundle-Localization: OSGI-INF/l10n/plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-17
@@ -15,50 +15,50 @@
  org.eclipse.jetty.server.handler;version="[12.0.0,13.0.0)",
  org.eclipse.jetty.util;version="[12.0.0,13.0.0)",
  org.eclipse.jetty.util.component;version="[12.0.0,13.0.0)",
- org.eclipse.jgit.api;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.api.errors;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.archive;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.awtui;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.blame;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.diff;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.dircache;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.errors;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.gitrepo;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal.diffmergetool;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal.storage.file;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal.storage.io;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal.storage.midx;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal.storage.pack;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal.storage.reftable;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.lfs;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.lfs.server;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.lfs.server.fs;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.lfs.server.s3;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.lib;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.lib.internal;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.merge;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.nls;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.notes;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.revplot;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.revwalk;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.revwalk.filter;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.storage.file;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.storage.pack;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.transport;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.transport.http.apache;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.transport.resolver;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.transport.ssh.jsch;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.transport.sshd;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.treewalk;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.treewalk.filter;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.util;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.util.io;version="[7.4.1,7.5.0)",
+ org.eclipse.jgit.api;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.api.errors;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.archive;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.awtui;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.blame;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.diff;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.dircache;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.errors;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.gitrepo;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal.diffmergetool;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal.storage.file;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal.storage.io;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal.storage.midx;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal.storage.reftable;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.lfs;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.lfs.server;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.lfs.server.fs;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.lfs.server.s3;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.lib;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.lib.internal;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.merge;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.nls;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.notes;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.revplot;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.revwalk;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.revwalk.filter;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.storage.file;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.storage.pack;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.transport;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.transport.http.apache;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.transport.resolver;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.transport.ssh.jsch;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.transport.sshd;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.treewalk;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.treewalk.filter;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.util;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.util.io;version="[7.5.0,7.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="7.4.1";
+Export-Package: org.eclipse.jgit.console;version="7.5.0";
  uses:="org.eclipse.jgit.transport,
   org.eclipse.jgit.util",
- org.eclipse.jgit.pgm;version="7.4.1";
+ org.eclipse.jgit.pgm;version="7.5.0";
   uses:="org.eclipse.jgit.transport,
    org.eclipse.jgit.util.io,
    org.eclipse.jgit.awtui,
@@ -70,14 +70,14 @@
    org.eclipse.jgit.treewalk,
    org.eclipse.jgit.api,
    javax.swing",
- org.eclipse.jgit.pgm.debug;version="7.4.1";
+ org.eclipse.jgit.pgm.debug;version="7.5.0";
   uses:="org.eclipse.jgit.util.io,
    org.eclipse.jgit.pgm,
    org.eclipse.jetty.servlet",
- org.eclipse.jgit.pgm.internal;version="7.4.1";
+ org.eclipse.jgit.pgm.internal;version="7.5.0";
   x-friends:="org.eclipse.jgit.pgm.test,
    org.eclipse.jgit.test",
- org.eclipse.jgit.pgm.opt;version="7.4.1";
+ org.eclipse.jgit.pgm.opt;version="7.5.0";
   uses:="org.kohsuke.args4j,
    org.eclipse.jgit.lib,
    org.eclipse.jgit.revwalk,
diff --git a/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF
index a4a81c7..36b0d49 100644
--- a/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF
@@ -3,6 +3,6 @@
 Bundle-Name: org.eclipse.jgit.pgm - Sources
 Bundle-SymbolicName: org.eclipse.jgit.pgm.source
 Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 7.4.1.qualifier
+Bundle-Version: 7.5.0.qualifier
 Bundle-SCM: url=https://github.com/eclipse-jgit/jgit, connection=scm:git:https://eclipse.gerrithub.io/eclipse-jgit/jgit.git, developerConnection=scm:git:https://eclipse.gerrithub.io/a/eclipse-jgit/jgit.git
-Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="7.4.1.qualifier";roots="."
+Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="7.5.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.pgm/pom.xml b/org.eclipse.jgit.pgm/pom.xml
index 2902626..0276ef9 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>7.4.1-SNAPSHOT</version>
+    <version>7.5.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.pgm</artifactId>
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Checkout.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Checkout.java
index 7a218ec..081db18 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Checkout.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Checkout.java
@@ -96,13 +96,11 @@ protected void run() throws Exception {
 							CLIText.get().switchedToBranch,
 							Repository.shortenRefName(ref.getName())));
 			} catch (InvalidRefNameException e){
-				if (name == null){
-					throw die(MessageFormat
-							.format("a valid ref is expected",e));
-				} else {
-					throw die(MessageFormat
-							.format(CLIText.get().notAValidRefName, name, e));
+				if (name == null) {
+					return;
 				}
+				throw die(MessageFormat.format(CLIText.get().notAValidRefName,
+						name, e));
 			}
 			catch (RefNotFoundException e) {
 				throw die(MessageFormat
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/MultiPackIndex.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/MultiPackIndex.java
index 1844223..a7df4fa 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/MultiPackIndex.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/MultiPackIndex.java
@@ -16,8 +16,7 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.LinkedHashMap;
 
 import org.eclipse.jgit.internal.storage.file.ObjectDirectory;
 import org.eclipse.jgit.internal.storage.file.Pack;
@@ -87,7 +86,7 @@ private void writeMultiPackIndex() throws IOException {
 
 		ObjectDirectory odb = (ObjectDirectory) db.getObjectDatabase();
 
-		Map<String, PackIndex> indexes = new HashMap<>();
+		LinkedHashMap<String, PackIndex> indexes = new LinkedHashMap<>();
 		for (Pack pack : odb.getPacks()) {
 			PackFile packFile = pack.getPackFile().create(PackExt.INDEX);
 			try {
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/ObjectSizeIndex.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/ObjectSizeIndex.java
index a40c396..492e273 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/ObjectSizeIndex.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/ObjectSizeIndex.java
@@ -69,7 +69,8 @@ private void checkObjectSizeIndex() throws IOException {
 			String name = pack.getPackName();
 			boolean hasObjectSizeIndex = pack.hasObjectSizeIndex();
 			outw.println(
-					String.format("  - %s - %b", name, hasObjectSizeIndex));
+					String.format("  - %s - %b", name,
+							Boolean.valueOf(hasObjectSizeIndex)));
 		}
 	}
 
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 f788a40..9841deb 100644
--- a/org.eclipse.jgit.ssh.apache.agent/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ssh.apache.agent/META-INF/MANIFEST.MF
@@ -2,17 +2,17 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %Bundle-Name
 Bundle-SymbolicName: org.eclipse.jgit.ssh.apache.agent;singleton:=true
-Bundle-Version: 7.4.1.qualifier
+Bundle-Version: 7.5.0.qualifier
 Bundle-Localization: OSGI-INF/l10n/agent
 Bundle-Vendor: %Bundle-Vendor
-Fragment-Host: org.eclipse.jgit.ssh.apache;bundle-version="[7.4.1,7.5.0)"
+Fragment-Host: org.eclipse.jgit.ssh.apache;bundle-version="[7.5.0,7.6.0)"
 Bundle-ActivationPolicy: lazy
 Automatic-Module-Name: org.eclipse.jgit.ssh.apache.agent
 Bundle-RequiredExecutionEnvironment: JavaSE-17
 Bundle-SCM: url=https://github.com/eclipse-jgit/jgit, connection=scm:git:https://eclipse.gerrithub.io/eclipse-jgit/jgit.git, developerConnection=scm:git:https://eclipse.gerrithub.io/a/eclipse-jgit/jgit.git
-Import-Package: org.eclipse.jgit.transport.sshd;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.nls;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.util;version="[7.4.1,7.5.0)"
+Import-Package: org.eclipse.jgit.transport.sshd;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.nls;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.util;version="[7.5.0,7.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="7.4.1";x-internal:=true
+Export-Package: org.eclipse.jgit.internal.transport.sshd.agent.connector;version="7.5.0";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 ba3d6a6..5a0c41a 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,6 +3,6 @@
 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: 7.4.1.qualifier
+Bundle-Version: 7.5.0.qualifier
 Bundle-SCM: url=https://github.com/eclipse-jgit/jgit, connection=scm:git:https://eclipse.gerrithub.io/eclipse-jgit/jgit.git, developerConnection=scm:git:https://eclipse.gerrithub.io/a/eclipse-jgit/jgit.git
-Eclipse-SourceBundle: org.eclipse.jgit.ssh.apache.agent;version="7.4.1.qualifier";roots="."
+Eclipse-SourceBundle: org.eclipse.jgit.ssh.apache.agent;version="7.5.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.ssh.apache.agent/pom.xml b/org.eclipse.jgit.ssh.apache.agent/pom.xml
index 8af671a..5c9863f 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>7.4.1-SNAPSHOT</version>
+    <version>7.5.0-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 a978990..c10f613 100644
--- a/org.eclipse.jgit.ssh.apache.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ssh.apache.test/META-INF/MANIFEST.MF
@@ -3,13 +3,13 @@
 Bundle-Name: %Bundle-Name
 Automatic-Module-Name: org.eclipse.jgit.ssh.apache.test
 Bundle-SymbolicName: org.eclipse.jgit.ssh.apache.test
-Bundle-Version: 7.4.1.qualifier
+Bundle-Version: 7.5.0.qualifier
 Bundle-Vendor: %Bundle-Vendor
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-17
 Bundle-SCM: url=https://github.com/eclipse-jgit/jgit, connection=scm:git:https://eclipse.gerrithub.io/eclipse-jgit/jgit.git, developerConnection=scm:git:https://eclipse.gerrithub.io/a/eclipse-jgit/jgit.git
-Require-Bundle: org.hamcrest.core;bundle-version="[1.3.0,2.0.0)"
-Import-Package: org.apache.sshd.certificate;version="[2.16.0,2.17.0)",
+Import-Package: org.apache.sshd.agent;version="[2.16.0,2.17.0)",
+ org.apache.sshd.certificate;version="[2.16.0,2.17.0)",
  org.apache.sshd.client.config.hosts;version="[2.16.0,2.17.0)",
  org.apache.sshd.common;version="[2.16.0,2.17.0)",
  org.apache.sshd.common.auth;version="[2.16.0,2.17.0)",
@@ -26,22 +26,23 @@
  org.apache.sshd.core;version="[2.16.0,2.17.0)",
  org.apache.sshd.server;version="[2.16.0,2.17.0)",
  org.apache.sshd.server.forward;version="[2.16.0,2.17.0)",
- org.eclipse.jgit.annotations;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.api;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.api.errors;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.errors;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal.signing.ssh;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal.storage.file;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal.transport.sshd;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal.transport.sshd.proxy;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.junit;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.junit.ssh;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.lib;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.revwalk;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.transport;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.transport.sshd;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.transport.sshd.agent;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.util;version="[7.4.1,7.5.0)",
+ org.eclipse.jgit.annotations;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.api;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.api.errors;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.errors;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal.signing.ssh;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal.storage.file;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal.transport.sshd;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal.transport.sshd.proxy;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.junit;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.junit.ssh;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.lib;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.revwalk;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.transport;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.transport.sshd;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.transport.sshd.agent;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.util;version="[7.5.0,7.6.0)",
+ org.hamcrest;version="[3.0.0,4.0.0)",
  org.junit;version="[4.13,5.0.0)",
  org.junit.experimental.theories;version="[4.13,5.0.0)",
  org.junit.rules;version="[4.13.0,5.0.0)",
diff --git a/org.eclipse.jgit.ssh.apache.test/pom.xml b/org.eclipse.jgit.ssh.apache.test/pom.xml
index 6a1fd60..ee33400 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>7.4.1-SNAPSHOT</version>
+    <version>7.5.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.ssh.apache.test</artifactId>
diff --git a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshTest.java b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshTest.java
index 8739457..98478e5 100644
--- a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshTest.java
+++ b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018, 2022 Thomas Wolf <thomas.wolf@paranor.ch> and others
+ * Copyright (C) 2018, 2025 Thomas Wolf <thomas.wolf@paranor.ch> and others
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -16,8 +16,10 @@
 import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 
+import java.io.BufferedInputStream;
 import java.io.BufferedWriter;
 import java.io.File;
+import java.io.InputStream;
 import java.io.IOException;
 import java.io.UncheckedIOException;
 import java.net.URISyntaxException;
@@ -26,12 +28,17 @@
 import java.nio.file.StandardOpenOption;
 import java.security.KeyPair;
 import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
 import java.security.PublicKey;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.stream.Collectors;
 
+import org.apache.sshd.agent.SshAgentConstants;
 import org.apache.sshd.client.config.hosts.KnownHostEntry;
 import org.apache.sshd.client.config.hosts.KnownHostHashValue;
 import org.apache.sshd.common.NamedFactory;
@@ -43,7 +50,12 @@
 import org.apache.sshd.common.kex.DHFactory;
 import org.apache.sshd.common.kex.KeyExchangeFactory;
 import org.apache.sshd.common.session.Session;
+import org.apache.sshd.common.signature.BuiltinSignatures;
+import org.apache.sshd.common.signature.Signature;
+import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
 import org.apache.sshd.common.util.net.SshdSocketAddress;
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.apache.sshd.core.CoreModuleProperties;
 import org.apache.sshd.server.ServerAuthenticationManager;
 import org.apache.sshd.server.ServerBuilder;
 import org.apache.sshd.server.SshServer;
@@ -55,6 +67,8 @@
 import org.eclipse.jgit.transport.RemoteSession;
 import org.eclipse.jgit.transport.SshSessionFactory;
 import org.eclipse.jgit.transport.URIish;
+import org.eclipse.jgit.transport.sshd.agent.Connector;
+import org.eclipse.jgit.transport.sshd.agent.ConnectorFactory;
 import org.eclipse.jgit.util.FS;
 import org.junit.Test;
 import org.junit.experimental.theories.Theories;
@@ -63,13 +77,13 @@
 @RunWith(Theories.class)
 public class ApacheSshTest extends SshTestBase {
 
-	@Override
-	protected SshSessionFactory createSessionFactory() {
+	private SshSessionFactory createSessionFactory(
+			ConnectorFactory agentFactory) {
 		return new SshdSessionFactoryBuilder()
 				// No proxies in tests
 				.setProxyDataFactory(null)
 				// No ssh-agent in tests
-				.setConnectorFactory(null)
+				.setConnectorFactory(agentFactory)
 				// The home directory is mocked at this point!
 				.setHomeDirectory(FS.DETECTED.userHome())
 				.setSshDirectory(sshDir)
@@ -77,6 +91,11 @@ protected SshSessionFactory createSessionFactory() {
 	}
 
 	@Override
+	protected SshSessionFactory createSessionFactory() {
+		return createSessionFactory(null);
+	}
+
+	@Override
 	protected void installConfig(String... config) {
 		File configFile = new File(sshDir, Constants.CONFIG);
 		if (config != null) {
@@ -133,6 +152,35 @@ public void testWrongKeyFirst() throws Exception {
 				"IdentityFile " + userKey.getAbsolutePath());
 	}
 
+	/**
+	 * Test for ext-info-c being sent. Try authenticating first with a wrong RSA
+	 * key. If ext-info-c is not set, the client will re-try three times with
+	 * the wrong key (once for each RSA signature algorithm). Since we set the
+	 * server to disconnect after three failed attempts, the test will fail. If
+	 * ext-info-c _is_ sent, the client will try only once and then try the
+	 * correct ed25519 key next and will succeed.
+	 *
+	 * @throws Exception
+	 *             on errors
+	 */
+	@Test
+	public void testKexExtension() throws Exception {
+		File userKey = new File(getTemporaryDirectory(), "userkey");
+		copyTestResource("id_ed25519", userKey);
+		File publicKey = new File(getTemporaryDirectory(), "userkey.pub");
+		copyTestResource("id_ed25519.pub", publicKey);
+		server.setTestUserPublicKey(publicKey.toPath());
+		CoreModuleProperties.MAX_AUTH_REQUESTS.set(server.getPropertyResolver(),
+				Integer.valueOf(3));
+		cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, //
+				"Host git", //
+				"HostName localhost", //
+				"Port " + testPort, //
+				"User " + TEST_USER, //
+				"IdentityFile " + privateKey1.getAbsolutePath(), // RSA
+				"IdentityFile " + userKey.getAbsolutePath());
+	}
+
 	@Test
 	public void testHashedKnownHosts() throws Exception {
 		assertTrue("Failed to delete known_hosts", knownHosts.delete());
@@ -894,4 +942,182 @@ public void testCipherModificationUnknown() throws Exception {
 						"Ciphers 3des-cbc"));
 		assertTrue(e.getLocalizedMessage().contains("3des-cbc"));
 	}
+
+	/**
+	 * Tests that the client does not try agent keys in an arbitrary order. It
+	 * should try agent keys that correspond to a listed IdentityFile first.
+	 *
+	 * @throws Exception
+	 *             on errors
+	 */
+	@Test
+	public void testAgentWithIdentities() throws Exception {
+		try (FakeAgentConnector fakeAgent = new FakeAgentConnector()) {
+			// Fill the agent with a few fake RSA key pairs
+			KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
+			generator.initialize(1024);
+			fakeAgent.add(generator.generateKeyPair());
+			fakeAgent.add(generator.generateKeyPair());
+			fakeAgent.add(generator.generateKeyPair());
+			fakeAgent.add(generator.generateKeyPair());
+
+			File userKey = new File(getTemporaryDirectory(), "userkey");
+			copyTestResource("id_ed25519", userKey);
+			File publicKey = new File(getTemporaryDirectory(), "userkey.pub");
+			copyTestResource("id_ed25519.pub", publicKey);
+			server.setTestUserPublicKey(publicKey.toPath());
+			try (InputStream in = new BufferedInputStream(
+					Files.newInputStream(userKey.toPath()))) {
+				Iterable<KeyPair> pairs = SecurityUtils
+						.loadKeyPairIdentities(null, null, in, null);
+				fakeAgent.add(pairs.iterator().next());
+			}
+
+			ConnectorFactory fakeFactory = new FakeConnectorFactory() {
+
+				@Override
+				public Connector create(String identityAgent, File homeDir)
+						throws IOException {
+					return fakeAgent;
+				}
+			};
+			SshSessionFactory.setInstance(createSessionFactory(fakeFactory));
+			CoreModuleProperties.MAX_AUTH_REQUESTS
+					.set(server.getPropertyResolver(), Integer.valueOf(2));
+			cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, //
+					"Host git", //
+					"HostName localhost", //
+					"Port " + testPort, //
+					"User " + TEST_USER, //
+					"IdentityFile " + userKey.getAbsolutePath());
+			assertTrue("Agent should have been called", fakeAgent.signCalled);
+		}
+	}
+
+	/**
+	 * A little dummy implementation of an SSH agent for testing.
+	 */
+	private static class FakeAgentConnector implements Connector {
+
+		private Map<String, KeyPair> keys = new LinkedHashMap<>();
+
+		boolean signCalled;
+
+		void add(KeyPair pair) {
+			keys.put(KeyUtils.getFingerPrint(pair.getPublic()), pair);
+		}
+
+		@Override
+		public void close() throws IOException {
+			// Nothing to do
+		}
+
+		@Override
+		public boolean connect() throws IOException {
+			return true;
+		}
+
+		@Override
+		public byte[] rpc(byte command, byte[] message) throws IOException {
+			switch (command) {
+			case SshAgentConstants.SSH2_AGENTC_REQUEST_IDENTITIES:
+				return list();
+			case SshAgentConstants.SSH2_AGENTC_SIGN_REQUEST:
+				signCalled = true;
+				return sign(message);
+			default:
+				return new byte[] { SshAgentConstants.SSH_AGENT_SUCCESS };
+			}
+		}
+
+		private byte[] list() {
+			ByteArrayBuffer result = new ByteArrayBuffer();
+			result.putByte(SshAgentConstants.SSH2_AGENT_IDENTITIES_ANSWER);
+			result.putInt(keys.size());
+			for (KeyPair pair : keys.values()) {
+				result.putPublicKey(pair.getPublic());
+				result.putString(""); // Comment
+			}
+			return result.getCompactData();
+		}
+
+		private byte[] sign(byte[] message) {
+			ByteArrayBuffer buf = new ByteArrayBuffer(message, 5,
+					message.length - 5);
+			try {
+				PublicKey pk = buf.getPublicKey();
+				byte[] dataToSign = buf.getBytes();
+				int flags = buf.getInt();
+				KeyPair pair = keys.get(KeyUtils.getFingerPrint(pk));
+				if (pair == null) {
+					return new byte[] { SshAgentConstants.SSH_AGENT_FAILURE };
+				}
+				// Figure out key type and signature
+				PrivateKey sk = pair.getPrivate();
+				String keyType = KeyUtils.getKeyType(sk);
+				String signatureAlgorithm = keyType;
+				// We ignore complications for sk-keys or certificates here
+				if (keyType.equals("ssh-rsa")) {
+					switch (flags & 6) {
+					case 2:
+						signatureAlgorithm = KeyUtils.RSA_SHA256_KEY_TYPE_ALIAS;
+						break;
+					case 4:
+						signatureAlgorithm = KeyUtils.RSA_SHA512_KEY_TYPE_ALIAS;
+						break;
+					default:
+						break;
+					}
+				}
+				Signature signer = BuiltinSignatures
+						.fromFactoryName(signatureAlgorithm).create();
+				signer.initSigner(null, sk);
+				signer.update(null, dataToSign);
+				ByteArrayBuffer sig = new ByteArrayBuffer();
+				sig.putString(signatureAlgorithm);
+				sig.putBytes(signer.sign(null));
+				ByteArrayBuffer result = new ByteArrayBuffer();
+				result.putByte(SshAgentConstants.SSH2_AGENT_SIGN_RESPONSE);
+				result.putBytes(sig.getCompactData());
+				return result.getCompactData();
+			} catch (Exception e) {
+				return new byte[] { SshAgentConstants.SSH_AGENT_FAILURE };
+			}
+		}
+	}
+
+	abstract static class FakeConnectorFactory implements ConnectorFactory {
+
+		@Override
+		public boolean isSupported() {
+			return true;
+		}
+
+		@Override
+		public String getName() {
+			return "fake";
+		}
+
+		@Override
+		public Collection<ConnectorDescriptor> getSupportedConnectors() {
+			return Collections.singleton(getDefaultConnector());
+		}
+
+		@Override
+		public ConnectorDescriptor getDefaultConnector() {
+			return new ConnectorDescriptor() {
+
+				@Override
+				public String getIdentityAgent() {
+					return "fake";
+				}
+
+				@Override
+				public String getDisplayName() {
+					return "fake";
+				}
+			};
+		}
+
+	}
 }
diff --git a/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF b/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF
index 6184991..8879e4c 100644
--- a/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF
@@ -6,11 +6,11 @@
 Bundle-Vendor: %Bundle-Vendor
 Bundle-Localization: OSGI-INF/l10n/plugin
 Bundle-ActivationPolicy: lazy
-Bundle-Version: 7.4.1.qualifier
+Bundle-Version: 7.5.0.qualifier
 Bundle-RequiredExecutionEnvironment: JavaSE-17
 Bundle-SCM: url=https://github.com/eclipse-jgit/jgit, connection=scm:git:https://eclipse.gerrithub.io/eclipse-jgit/jgit.git, developerConnection=scm:git:https://eclipse.gerrithub.io/a/eclipse-jgit/jgit.git
-Export-Package: org.eclipse.jgit.internal.signing.ssh;version="7.4.1";x-friends:="org.eclipse.jgit.ssh.apache.test",
- org.eclipse.jgit.internal.transport.sshd;version="7.4.1";x-friends:="org.eclipse.jgit.ssh.apache.test";
+Export-Package: org.eclipse.jgit.internal.signing.ssh;version="7.5.0";x-friends:="org.eclipse.jgit.ssh.apache.test",
+ org.eclipse.jgit.internal.transport.sshd;version="7.5.0";x-friends:="org.eclipse.jgit.ssh.apache.test";
   uses:="org.apache.sshd.client,
    org.apache.sshd.client.auth,
    org.apache.sshd.client.auth.keyboard,
@@ -25,19 +25,19 @@
    org.apache.sshd.common.signature,
    org.apache.sshd.common.util.buffer,
    org.eclipse.jgit.transport",
- org.eclipse.jgit.internal.transport.sshd.agent;version="7.4.1";x-internal:=true,
- org.eclipse.jgit.internal.transport.sshd.auth;version="7.4.1";x-internal:=true,
- org.eclipse.jgit.internal.transport.sshd.pkcs11;version="7.4.1";x-internal:=true,
- org.eclipse.jgit.internal.transport.sshd.proxy;version="7.4.1";x-friends:="org.eclipse.jgit.ssh.apache.test",
- org.eclipse.jgit.signing.ssh;version="7.4.1";uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.transport.sshd;version="7.4.1";
+ org.eclipse.jgit.internal.transport.sshd.agent;version="7.5.0";x-internal:=true,
+ org.eclipse.jgit.internal.transport.sshd.auth;version="7.5.0";x-internal:=true,
+ org.eclipse.jgit.internal.transport.sshd.pkcs11;version="7.5.0";x-internal:=true,
+ org.eclipse.jgit.internal.transport.sshd.proxy;version="7.5.0";x-friends:="org.eclipse.jgit.ssh.apache.test",
+ org.eclipse.jgit.signing.ssh;version="7.5.0";uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.transport.sshd;version="7.5.0";
   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="7.4.1"
+ org.eclipse.jgit.transport.sshd.agent;version="7.5.0"
 Import-Package: org.bouncycastle.jce.provider;version="[1.80.0,2.0.0)",
  org.apache.sshd.agent;version="[2.16.0,2.17.0)",
  org.apache.sshd.client;version="[2.16.0,2.17.0)",
@@ -91,14 +91,14 @@
  org.apache.sshd.sftp;version="[2.16.0,2.17.0)",
  org.apache.sshd.sftp.client;version="[2.16.0,2.17.0)",
  org.apache.sshd.sftp.common;version="[2.16.0,2.17.0)",
- org.eclipse.jgit.annotations;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.api.errors;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.errors;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.fnmatch;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal.storage.file;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal.transport.ssh;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.lib;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.nls;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.transport;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.util;version="[7.4.1,7.5.0)",
+ org.eclipse.jgit.annotations;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.api.errors;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.errors;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.fnmatch;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal.storage.file;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal.transport.ssh;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.lib;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.nls;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.transport;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.util;version="[7.5.0,7.6.0)",
  org.slf4j;version="[1.7.0,3.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 616eea4..bd2faa5 100644
--- a/org.eclipse.jgit.ssh.apache/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.ssh.apache/META-INF/SOURCE-MANIFEST.MF
@@ -3,6 +3,6 @@
 Bundle-Name: org.eclipse.jgit.ssh.apache - Sources
 Bundle-SymbolicName: org.eclipse.jgit.ssh.apache.source
 Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 7.4.1.qualifier
+Bundle-Version: 7.5.0.qualifier
 Bundle-SCM: url=https://github.com/eclipse-jgit/jgit, connection=scm:git:https://eclipse.gerrithub.io/eclipse-jgit/jgit.git, developerConnection=scm:git:https://eclipse.gerrithub.io/a/eclipse-jgit/jgit.git
-Eclipse-SourceBundle: org.eclipse.jgit.ssh.apache;version="7.4.1.qualifier";roots="."
+Eclipse-SourceBundle: org.eclipse.jgit.ssh.apache;version="7.5.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.ssh.apache/pom.xml b/org.eclipse.jgit.ssh.apache/pom.xml
index 5dcee13..77e4756 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>7.4.1-SNAPSHOT</version>
+    <version>7.5.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.ssh.apache</artifactId>
diff --git a/org.eclipse.jgit.ssh.apache/resources/org/eclipse/jgit/internal/transport/sshd/SshdText.properties b/org.eclipse.jgit.ssh.apache/resources/org/eclipse/jgit/internal/transport/sshd/SshdText.properties
index 773c4b9..c91a64a 100644
--- a/org.eclipse.jgit.ssh.apache/resources/org/eclipse/jgit/internal/transport/sshd/SshdText.properties
+++ b/org.eclipse.jgit.ssh.apache/resources/org/eclipse/jgit/internal/transport/sshd/SshdText.properties
@@ -69,6 +69,7 @@
 knownHostsUserAskCreationMsg=File {0} does not exist.
 knownHostsUserAskCreationPrompt=Create file {0} ?
 loginDenied=Cannot log in at {0}:{1}
+noExplicitKeys=Host config for {0} has 'IdentitiesOnly yes' but no valid 'IdentityFile' configured.
 passwordPrompt=Password
 pkcs11Error=ERROR: {0}
 pkcs11FailedInstantiation=HostConfig for host {0} (hostname {1}): could not instantiate {2} {3}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthentication.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthentication.java
index 6aace47..7aaa5c2 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthentication.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthentication.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018, 2023 Thomas Wolf <twolf@apache.org> and others
+ * Copyright (C) 2018, 2025 Thomas Wolf <twolf@apache.org> and others
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -32,6 +32,7 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Iterator;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.NoSuchElementException;
@@ -381,60 +382,39 @@ protected Iterable<KeyAgentIdentity> initializeAgentIdentities(
 			if (allAgentKeys == null) {
 				return null;
 			}
-			Collection<PublicKey> identityFiles = identitiesOnly();
-			if (GenericUtils.isEmpty(identityFiles)) {
+			if (hostConfig == null) {
+				return allAgentKeys;
+			}
+			Collection<PublicKey> explicitKeys = getExplicitKeys(
+					hostConfig.getIdentities());
+			if (GenericUtils.isEmpty(explicitKeys)) {
+				if (hostConfig.isIdentitiesOnly()) {
+					log.warn(LOG_FORMAT, format(SshdText.get().noExplicitKeys,
+							hostConfig.getHost()));
+					return Collections.emptyList();
+				}
 				return allAgentKeys;
 			}
 
-			// Only consider agent or PKCS11 keys that match a known public key
-			// file.
-			return () -> new Iterator<>() {
-
-				private final Iterator<KeyAgentIdentity> identities = allAgentKeys
-						.iterator();
-
-				private KeyAgentIdentity next;
-
-				@Override
-				public boolean hasNext() {
-					while (next == null && identities.hasNext()) {
-						KeyAgentIdentity val = identities.next();
-						PublicKey pk = val.getKeyIdentity().getPublic();
-						// This checks against all explicit keys for any agent
-						// key, but since identityFiles.size() is typically 1,
-						// it should be fine.
-						if (identityFiles.stream()
-								.anyMatch(k -> KeyUtils.compareKeys(k, pk))) {
-							next = val;
-							return true;
-						}
-						if (log.isTraceEnabled()) {
-							log.trace(
-									"Ignoring SSH agent or PKCS11 {} key not in explicit IdentityFile in SSH config: {}", //$NON-NLS-1$
-									KeyUtils.getKeyType(pk),
-									KeyUtils.getFingerPrint(pk));
-						}
-					}
-					return next != null;
+			// Sort the identities such that the ones for the explicitly listed
+			// keys come first, in the order listed.
+			Map<String, KeyAgentIdentity> fromAgent = new LinkedHashMap<>();
+			allAgentKeys.forEach(k -> fromAgent.computeIfAbsent(
+					KeyUtils.getFingerPrint(k.getKeyIdentity().getPublic()),
+					x -> k));
+			List<KeyAgentIdentity> result = new ArrayList<>();
+			for (PublicKey pk : explicitKeys) {
+				KeyAgentIdentity id = fromAgent
+						.remove(KeyUtils.getFingerPrint(pk));
+				if (id != null) {
+					result.add(id);
 				}
-
-				@Override
-				public KeyAgentIdentity next() {
-					if (!hasNext()) {
-						throw new NoSuchElementException();
-					}
-					KeyAgentIdentity result = next;
-					next = null;
-					return result;
-				}
-			};
-		}
-
-		private Collection<PublicKey> identitiesOnly() {
-			if (hostConfig != null && hostConfig.isIdentitiesOnly()) {
-				return getExplicitKeys(hostConfig.getIdentities());
 			}
-			return Collections.emptyList();
+			if (!hostConfig.isIdentitiesOnly()) {
+				result.addAll(fromAgent.values());
+			}
+
+			return result;
 		}
 
 		private Iterable<KeyAgentIdentity> getAgentIdentities()
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/SshdText.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/SshdText.java
index e401378..d40ec83 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/SshdText.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/SshdText.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018, 2024 Thomas Wolf <twolf@apache.org> and others
+ * Copyright (C) 2018, 2025 Thomas Wolf <twolf@apache.org> and others
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -91,6 +91,7 @@ public static SshdText get() {
 	/***/ public String knownHostsUserAskCreationMsg;
 	/***/ public String knownHostsUserAskCreationPrompt;
 	/***/ public String loginDenied;
+	/***/ public String noExplicitKeys;
 	/***/ public String passwordPrompt;
 	/***/ public String pkcs11Error;
 	/***/ public String pkcs11FailedInstantiation;
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 4f1a737..5823845 100644
--- a/org.eclipse.jgit.ssh.jsch.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ssh.jsch.test/META-INF/MANIFEST.MF
@@ -3,22 +3,21 @@
 Bundle-Name: %Bundle-Name
 Automatic-Module-Name: org.eclipse.jgit.ssh.jsch.test
 Bundle-SymbolicName: org.eclipse.jgit.ssh.jsch.test
-Bundle-Version: 7.4.1.qualifier
+Bundle-Version: 7.5.0.qualifier
 Bundle-Vendor: %Bundle-Vendor
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-17
 Bundle-SCM: url=https://github.com/eclipse-jgit/jgit, connection=scm:git:https://eclipse.gerrithub.io/eclipse-jgit/jgit.git, developerConnection=scm:git:https://eclipse.gerrithub.io/a/eclipse-jgit/jgit.git
-Require-Bundle: org.hamcrest.core;bundle-version="[1.3.0,2.0.0)"
 Import-Package: com.jcraft.jsch;version="[0.1.54,0.2.0)",
- org.eclipse.jgit.errors;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal.storage.file;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.junit;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.junit.ssh;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.lib;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.transport;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.transport.ssh.jsch;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.util;version="[7.4.1,7.5.0)",
- org.hamcrest;version="[1.1.0,3.0.0)",
+ org.eclipse.jgit.errors;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal.storage.file;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.junit;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.junit.ssh;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.lib;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.transport;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.transport.ssh.jsch;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.util;version="[7.5.0,7.6.0)",
+ org.hamcrest;version="[3.0.0,4.0.0)",
  org.junit;version="[4.13,5.0.0)",
  org.junit.experimental.theories;version="[4.13,5.0.0)",
  org.junit.runner;version="[4.13,5.0.0)"
diff --git a/org.eclipse.jgit.ssh.jsch.test/pom.xml b/org.eclipse.jgit.ssh.jsch.test/pom.xml
index 9e9282a..467154f 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>7.4.1-SNAPSHOT</version>
+    <version>7.5.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.ssh.jsch.test</artifactId>
diff --git a/org.eclipse.jgit.ssh.jsch/META-INF/MANIFEST.MF b/org.eclipse.jgit.ssh.jsch/META-INF/MANIFEST.MF
index 5a16f58..0e09896 100644
--- a/org.eclipse.jgit.ssh.jsch/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ssh.jsch/META-INF/MANIFEST.MF
@@ -3,20 +3,20 @@
 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="[7.4.1,7.5.0)"
+Fragment-Host: org.eclipse.jgit;bundle-version="[7.5.0,7.6.0)"
 Bundle-Vendor: %Bundle-Vendor
 Bundle-Localization: OSGI-INF/l10n/jsch
 Bundle-ActivationPolicy: lazy
-Bundle-Version: 7.4.1.qualifier
+Bundle-Version: 7.5.0.qualifier
 Bundle-RequiredExecutionEnvironment: JavaSE-17
 Bundle-SCM: url=https://github.com/eclipse-jgit/jgit, connection=scm:git:https://eclipse.gerrithub.io/eclipse-jgit/jgit.git, developerConnection=scm:git:https://eclipse.gerrithub.io/a/eclipse-jgit/jgit.git
-Export-Package: org.eclipse.jgit.transport.ssh.jsch;version="7.4.1"
+Export-Package: org.eclipse.jgit.transport.ssh.jsch;version="7.5.0"
 Import-Package: com.jcraft.jsch;version="[0.1.37,0.2.0)",
- org.eclipse.jgit.errors;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal.transport.ssh;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.nls;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.transport;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.util;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.util.io;version="[7.4.1,7.5.0)",
+ org.eclipse.jgit.errors;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal.transport.ssh;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.nls;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.transport;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.util;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.util.io;version="[7.5.0,7.6.0)",
  org.slf4j;version="[1.7.0,3.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 25c136f..ba5619c 100644
--- a/org.eclipse.jgit.ssh.jsch/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.ssh.jsch/META-INF/SOURCE-MANIFEST.MF
@@ -3,6 +3,6 @@
 Bundle-Name: org.eclipse.jgit.ssh.jsch - Sources
 Bundle-SymbolicName: org.eclipse.jgit.ssh.jsch.source
 Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 7.4.1.qualifier
+Bundle-Version: 7.5.0.qualifier
 Bundle-SCM: url=https://github.com/eclipse-jgit/jgit, connection=scm:git:https://eclipse.gerrithub.io/eclipse-jgit/jgit.git, developerConnection=scm:git:https://eclipse.gerrithub.io/a/eclipse-jgit/jgit.git
-Eclipse-SourceBundle: org.eclipse.jgit.ssh.jsch;version="7.4.1.qualifier";roots="."
+Eclipse-SourceBundle: org.eclipse.jgit.ssh.jsch;version="7.5.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.ssh.jsch/pom.xml b/org.eclipse.jgit.ssh.jsch/pom.xml
index cb706c6..59d8b0b 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>7.4.1-SNAPSHOT</version>
+    <version>7.5.0-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 1f6d0eb..c724d35 100644
--- a/org.eclipse.jgit.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.test/META-INF/MANIFEST.MF
@@ -3,13 +3,11 @@
 Bundle-Name: %Bundle-Name
 Automatic-Module-Name: org.eclipse.jgit.test
 Bundle-SymbolicName: org.eclipse.jgit.test
-Bundle-Version: 7.4.1.qualifier
+Bundle-Version: 7.5.0.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %Bundle-Vendor
 Bundle-RequiredExecutionEnvironment: JavaSE-17
 Bundle-SCM: url=https://github.com/eclipse-jgit/jgit, connection=scm:git:https://eclipse.gerrithub.io/eclipse-jgit/jgit.git, developerConnection=scm:git:https://eclipse.gerrithub.io/a/eclipse-jgit/jgit.git
-Require-Bundle: org.hamcrest.core;bundle-version="[1.3.0,2.0.0)",
- org.hamcrest.library;bundle-version="[1.3.0,2.0.0)"
 Import-Package: com.googlecode.javaewah;version="[1.1.6,2.0.0)",
  net.bytebuddy.agent;version="[1.9.0,2.0.0)",
  net.bytebuddy.dynamic.loading;version="[1.9.0,2.0.0)",
@@ -23,66 +21,68 @@
  org.apache.commons.io.output;version="[2.15.0,3.0.0)",
  org.apache.commons.lang3;version="[3.17.0,4.0.0)",
  org.assertj.core.api;version="[3.14.0,4.0.0)",
- org.eclipse.jgit.annotations;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.api;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.api.errors;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.archive;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.attributes;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.awtui;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.blame;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.blame.cache;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.diff;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.dircache;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.errors;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.events;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.fnmatch;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.gitrepo;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.hooks;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.ignore;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.ignore.internal;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal.diff;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal.diffmergetool;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal.fsck;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal.revwalk;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal.storage.commitgraph;version="7.4.1",
- org.eclipse.jgit.internal.storage.dfs;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal.storage.file;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal.storage.io;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal.storage.memory;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal.storage.midx;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal.storage.pack;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal.storage.reftable;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal.transport.connectivity;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal.transport.http;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal.transport.parser;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.internal.transport.ssh;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.junit;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.junit.time;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.lfs;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.lib;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.lib.internal;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.logging;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.merge;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.nls;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.notes;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.patch;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.pgm;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.pgm.internal;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.revplot;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.revwalk;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.revwalk.filter;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.storage.file;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.storage.pack;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.submodule;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.transport;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.transport.http;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.transport.resolver;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.treewalk;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.treewalk.filter;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.util;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.util.io;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.util.sha1;version="[7.4.1,7.5.0)",
+ org.eclipse.jgit.annotations;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.api;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.api.errors;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.archive;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.attributes;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.awtui;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.blame;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.blame.cache;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.diff;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.dircache;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.errors;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.events;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.fnmatch;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.gitrepo;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.hooks;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.ignore;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.ignore.internal;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal.diff;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal.diffmergetool;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal.fsck;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal.revwalk;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal.storage.commitgraph;version="7.5.0",
+ org.eclipse.jgit.internal.storage.dfs;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal.storage.file;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal.storage.io;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal.storage.memory;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal.storage.midx;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal.storage.reftable;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal.transport.connectivity;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal.transport.http;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal.transport.parser;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.internal.transport.ssh;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.junit;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.junit.time;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.lfs;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.lib;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.lib.internal;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.logging;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.merge;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.nls;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.notes;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.patch;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.pgm;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.pgm.internal;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.revplot;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.revwalk;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.revwalk.filter;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.storage.file;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.storage.pack;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.submodule;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.transport;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.transport.http;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.transport.resolver;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.treewalk;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.treewalk.filter;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.util;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.util.io;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.util.sha1;version="[7.5.0,7.6.0)",
+ org.hamcrest;version="[3.0.0,4.0.0)",
+ org.hamcrest.collection;version="[3.0.0,4.0.0)",
  org.junit;version="[4.13,5.0.0)",
  org.junit.experimental.theories;version="[4.13,5.0.0)",
  org.junit.function;version="[4.13.0,5.0.0)",
diff --git a/org.eclipse.jgit.test/pom.xml b/org.eclipse.jgit.test/pom.xml
index b246680..1483319 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>7.4.1-SNAPSHOT</version>
+    <version>7.5.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.test</artifactId>
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/blame/BlameGeneratorCacheTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/blame/BlameGeneratorCacheTest.java
index 3e4ac1f..1f02a63 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/blame/BlameGeneratorCacheTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/blame/BlameGeneratorCacheTest.java
@@ -406,6 +406,7 @@ private void assertRegions(RevCommit commit, InMemoryBlameCache cache,
 		}
 	}
 
+	@SuppressWarnings("boxing")
 	private void assertCacheUsage(RevCommit commit, InMemoryBlameCache cache,
 			boolean cacheHit, int candidatesVisited) throws IOException {
 		try (BlameGenerator gen = new BlameGenerator(db, FILE, cache)) {
@@ -429,6 +430,7 @@ private static void assertAllLinesCovered(int lines,
 				regions.get(regions.size() - 1).resultEnd());
 	}
 
+	@SuppressWarnings("boxing")
 	private static void assertRegionsEquals(List<EmittedRegion> expected,
 			List<EmittedRegion> actual) {
 		assertEquals(expected.size(), actual.size());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/indexdiff/IndexDiffWithSymlinkTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/indexdiff/IndexDiffWithSymlinkTest.java
index 1119db3..c4efea5 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/indexdiff/IndexDiffWithSymlinkTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/indexdiff/IndexDiffWithSymlinkTest.java
@@ -65,6 +65,7 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.util.Arrays;
 import java.util.Collections;
 
 import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
@@ -79,6 +80,8 @@
 import org.eclipse.jgit.util.SystemReader;
 import org.junit.Before;
 import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * MacOS-only test for dealing with symlinks in IndexDiff. Recreates using cgit
@@ -89,6 +92,9 @@
  */
 public class IndexDiffWithSymlinkTest extends LocalDiskRepositoryTestCase {
 
+	private static final Logger LOG = LoggerFactory
+			.getLogger(IndexDiffWithSymlinkTest.class);
+
 	private static final String FILEREPO = "filerepo";
 
 	private static final String TESTFOLDER = "testfolder";
@@ -205,7 +211,17 @@ public void testSymlinkWithEncodingDifference() throws Exception {
 					linkTarget.toString());
 			raw = rawPath(linkTarget);
 			if (raw != null) {
-				assertArrayEquals("Expected an NFD link target", NFD, raw);
+				// Prior to Java 20, Java would convert to NFD on Mac to be
+				// compliant with HFS+. Since Java 20, it doesn't because APFS
+				// stores file names in NFC. See JDK-8289689.
+				if (Arrays.equals(NFD, raw)) {
+					LOG.info("Symlink was written by Java as NFD");
+				} else {
+					assertArrayEquals("Expected an NFC link target", NFC, raw);
+					LOG.info("Symlink was written by Java as NFC");
+				}
+			} else {
+				LOG.warn("Cannot determine raw symbolic link content");
 			}
 			// Do the indexdiff
 			WorkingTreeIterator iterator = new FileTreeIterator(testRepo);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsBlockMidxTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsBlockMidxTest.java
new file mode 100644
index 0000000..0e12707
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsBlockMidxTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2025, 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
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.storage.dfs;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+import org.eclipse.jgit.internal.storage.pack.PackExt;
+import org.eclipse.jgit.junit.JGitTestUtil;
+import org.eclipse.jgit.junit.TestRng;
+import org.junit.Test;
+
+public class DfsBlockMidxTest {
+
+	private static final DfsStreamKey key = DfsStreamKey
+			.of(new DfsRepositoryDescription("repo"), "pack-1", PackExt.PACK);;
+
+	@Test
+	public void block_boundaries() {
+		DfsStreamKey key = DfsStreamKey.of(new DfsRepositoryDescription("repo"),
+				"pack-1", PackExt.PACK);
+		DfsBlock block = new DfsBlock(key, 600, new byte[300]);
+
+		DfsBlockMidx midxBlock = new DfsBlockMidx(block, 1000);
+		assertEquals(block.start, midxBlock.start);
+		assertEquals(block.end, midxBlock.end);
+		assertEquals(block.stream, midxBlock.stream);
+	}
+
+	@Test
+	public void block_size() {
+		byte[] rawData = new TestRng(JGitTestUtil.getName()).nextBytes(300);
+		DfsBlock block600to900 = new DfsBlock(key, 600, rawData);
+		DfsBlockMidx midxBlock = new DfsBlockMidx(block600to900, 1000);
+		assertEquals(300, midxBlock.size());
+	}
+
+	@Test
+	public void block_copy_allBytesInBlock() {
+		byte[] rawData = new TestRng(JGitTestUtil.getName()).nextBytes(300);
+		// Block from positions 600 to 900 in a pack X
+		DfsBlock block600to900 = new DfsBlock(key, 600, rawData);
+		// Pack X starts at offset 1000 in the multipack index
+		DfsBlockMidx midxBlock = new DfsBlockMidx(block600to900, 1000);
+
+		int bytesToCopy = 50;
+		byte[] dest = new byte[bytesToCopy];
+		// Copy from midx(1700, 1750) => pack(700, 750) => block(100, 150)
+		int copied = midxBlock.copy(1700, dest, 0, bytesToCopy);
+
+		assertEquals(bytesToCopy, copied);
+		byte[] expected = Arrays.copyOfRange(rawData, 100, 150);
+		assertArrayEquals(expected, dest);
+	}
+
+	@Test
+	public void block_copy_notEnoughInBlock() {
+		byte[] rawData = new TestRng(JGitTestUtil.getName()).nextBytes(300);
+		DfsBlock block600to900 = new DfsBlock(key, 600, rawData);
+		DfsBlockMidx midxBlock = new DfsBlockMidx(block600to900, 1000);
+
+		int bytesToCopy = 500;
+		byte[] dest = new byte[bytesToCopy];
+		// Copy from midx(1700, 2100) => pack(700, 1100) => block(700, 900)
+		// (the block doesn't have all the bytes)
+		int r = midxBlock.copy(1700, dest, 0, bytesToCopy);
+
+		assertEquals(200, r);
+		byte[] expected = Arrays.copyOfRange(rawData, 100, 300);
+		assertArrayEqualSegment(expected, dest, 0, 200);
+	}
+
+	@Test
+	public void block_contains() {
+		byte[] rawData = new TestRng(JGitTestUtil.getName()).nextBytes(300);
+		DfsBlock block600to900 = new DfsBlock(key, 600, rawData);
+		DfsBlockMidx midxBlock = new DfsBlockMidx(block600to900, 1000);
+
+		assertTrue(midxBlock.contains(key, 1700));
+		assertFalse(midxBlock.contains(key, 2100));
+		assertFalse(midxBlock.contains(key, 699));
+	}
+
+	@Test
+	public void block_zeroCopyByteBuffer() {
+		byte[] rawData = new TestRng(JGitTestUtil.getName()).nextBytes(300);
+		DfsBlock block600to900 = new DfsBlock(key, 600, rawData);
+		ByteBuffer bb = block600to900.zeroCopyByteBuffer(54);
+
+		assertEquals(rawData[54], bb.get());
+	}
+
+	private static void assertArrayEqualSegment(byte[] expected, byte[] actual, int start, int end) {
+		assertTrue(start < end);
+		for (int i = start; i < end; i++) {
+			assertEquals("Diff at position" + i, expected[i], actual[i]);
+		}
+	}
+}
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 80bd689..b340636 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
@@ -4,6 +4,7 @@
 import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.GC_REST;
 import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.INSERT;
 import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.UNREACHABLE_GARBAGE;
+import static org.eclipse.jgit.internal.storage.pack.PackExt.MULTI_PACK_INDEX;
 import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
 import static org.eclipse.jgit.internal.storage.pack.PackExt.REFTABLE;
 import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH;
@@ -18,9 +19,12 @@
 import java.io.IOException;
 import java.time.Instant;
 import java.time.ZoneOffset;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.List;
 import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
 
 import org.eclipse.jgit.internal.storage.commitgraph.CommitGraph;
 import org.eclipse.jgit.internal.storage.commitgraph.CommitGraphWriter;
@@ -42,6 +46,7 @@
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectIdRef;
 import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.ProgressMonitor;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.RevBlob;
@@ -62,12 +67,15 @@ public class DfsGarbageCollectorTest {
 	private DfsObjDatabase odb;
 	private MockSystemReader mockSystemReader;
 
+	private static final ProgressMonitor NULL_PM = NullProgressMonitor.INSTANCE;
+
 	@Before
 	public void setUp() throws IOException {
 		DfsRepositoryDescription desc = new DfsRepositoryDescription("test");
 		git = new TestRepository<>(new InMemoryRepository(desc));
 		repo = git.getRepository();
 		odb = repo.getObjectDatabase();
+		odb.setUseMultipackIndex(true);
 		mockSystemReader = new MockSystemReader();
 		SystemReader.setInstance(mockSystemReader);
 	}
@@ -1227,6 +1235,117 @@ public void objectSizeIndex_unreachableGarbage_noIdx() throws Exception {
 	}
 
 	@Test
+	public void midx_oneMidx_deleteMidxs_allObjectsOneGC() throws Exception {
+		String master = "refs/heads/master";
+		RevCommit root = git.branch(master).commit().message("root").noParents()
+				.create();
+		git.branch(master).commit().message("commit on head")
+				.add("file.txt", git.blob("a blob")).parent(root).create();
+		assertEquals(3, countPacks(INSERT));
+
+		DfsPackDescription midx = DfsMidxWriter.writeMidx(
+				NullProgressMonitor.INSTANCE, odb,
+				Arrays.asList(odb.getPacks()), null);
+		odb.commitPack(List.of(midx), null);
+
+		gcNoTtl();
+
+		// Only one pack, is GC but not multipack index
+		assertEquals(1, odb.getPacks().length);
+		DfsPackDescription actualDesc = odb.getPacks()[0].getPackDescription();
+		assertEquals(GC, actualDesc.getPackSource());
+		assertFalse(actualDesc.hasFileExt(MULTI_PACK_INDEX));
+		DfsPackFile pack = odb.getPacks()[0];
+		assertFalse(pack instanceof DfsPackFileMidx);
+		assertTrue(isObjectInPack(root, pack));
+	}
+
+	@Test
+	public void midx_chainedMidx_deleteMidxs_allObjsInOneGC() throws Exception {
+		String master = "refs/heads/master";
+		List<RevCommit> knownCommits = new ArrayList<>(11);
+		RevCommit root = git.branch(master).commit().message("root").noParents()
+				.create();
+		knownCommits.add(root);
+		RevCommit tip = root;
+		for (int i = 0; i < 10; i++) {
+			tip = git.branch(master).commit().message("commit on head")
+					.add("file.txt", git.blob("a blob " + i)).parent(tip)
+					.create();
+			knownCommits.add(tip);
+			// Each of these creates two packs
+		}
+		assertEquals(21, countPacks(INSERT));
+
+		List<DfsPackFile> basicPacks = Arrays.stream(odb.getPacks())
+				.collect(Collectors.toUnmodifiableList());
+		DfsPackDescription midx = DfsMidxWriter.writeMidx(NULL_PM, odb,
+				basicPacks.subList(0, 9), null);
+		odb.commitPack(List.of(midx), null);
+
+		DfsPackDescription midx2 = DfsMidxWriter.writeMidx(NULL_PM, odb,
+				basicPacks.subList(9, 21), midx);
+		odb.commitPack(List.of(midx2), null);
+
+		// Verify we got one pack that is an midx
+		// This is testing the test code
+		assertEquals(1, odb.getPacks().length);
+		assertTrue(odb.getPacks()[0] instanceof DfsPackFileMidx);
+		DfsPackDescription theDesc = odb.getPacks()[0].getPackDescription();
+		assertTrue(theDesc.hasFileExt(MULTI_PACK_INDEX));
+		assertEquals(12, theDesc.getCoveredPacks().size());
+		assertEquals(theDesc.getMultiPackIndexBase(), midx);
+		assertEquals(9,
+				theDesc.getMultiPackIndexBase().getCoveredPacks().size());
+		gcNoTtl();
+
+		// One pack, GC WITHOUT multipack index, contains ALL objects
+		assertEquals(1, odb.getPacks().length);
+		DfsPackFile pack = odb.getPacks()[0];
+		assertEquals(GC, pack.getPackDescription().getPackSource());
+		assertFalse(pack instanceof DfsPackFileMidx);
+		assertFalse(pack.getPackDescription().hasFileExt(MULTI_PACK_INDEX));
+		for (RevCommit c : knownCommits) {
+			assertTrue(isObjectInPack(c, pack));
+		}
+	}
+
+	@Test
+	public void midx_packAndMidx_deleteMidxs_allObjectsOneGC()
+			throws Exception {
+		String master = "refs/heads/master";
+		RevCommit root = git.branch(master).commit().message("root").noParents()
+				.create();
+		RevCommit tip = git.branch(master).commit().message("commit on head")
+				.add("file.txt", git.blob("a blob")).parent(root).create();
+		assertEquals(3, countPacks(INSERT));
+
+		List<DfsPackFile> packs = Arrays.stream(odb.getPacks()).toList();
+		DfsPackDescription midx = DfsMidxWriter.writeMidx(NULL_PM, odb, packs,
+				null);
+		odb.commitPack(List.of(midx), null);
+
+		RevBlob blobOutOfMidx = git.blob("some content");
+		RevCommit commitOutOfMidx = git.branch(master).commit()
+				.message("an extra commit").add("other.txt", blobOutOfMidx)
+				.parent(tip).create();
+		assertEquals(3, odb.getPacks().length); // midx + 2 new packs
+		gcNoTtl();
+
+		// Only one pack, is GC but not multipack index
+		assertEquals(1, odb.getPacks().length);
+		DfsPackDescription actualDesc = odb.getPacks()[0].getPackDescription();
+		assertEquals(GC, actualDesc.getPackSource());
+		assertFalse(actualDesc.hasFileExt(MULTI_PACK_INDEX));
+
+		DfsPackFile pack = odb.getPacks()[0];
+		assertTrue(isObjectInPack(root, pack));
+		assertTrue(isObjectInPack(root, pack));
+		assertTrue(isObjectInPack(blobOutOfMidx, pack));
+		assertTrue(isObjectInPack(commitOutOfMidx, pack));
+	}
+
+	@Test
 	public void bitmapIndexWrittenDuringGc() throws Exception {
 		int numBranches = 2;
 		int commitsPerBranch = 50;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsObjDatabaseTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsObjDatabaseTest.java
new file mode 100644
index 0000000..c30753e
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsObjDatabaseTest.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2025, 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.dfs;
+
+import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.COMPACT;
+import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.GC;
+import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.INSERT;
+import static org.eclipse.jgit.internal.storage.pack.PackExt.MULTI_PACK_INDEX;
+import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
+import static org.eclipse.jgit.internal.storage.pack.PackExt.REFTABLE;
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.jgit.internal.storage.pack.PackExt;
+import org.junit.Before;
+import org.junit.Test;
+
+public class DfsObjDatabaseTest {
+	InMemoryRepository db;
+
+	private static final DfsRepositoryDescription repoDesc = new DfsRepositoryDescription(
+			"test");
+
+	@Before
+	public void setUp() {
+		db = new InMemoryRepository(repoDesc);
+	}
+
+	@Test
+	public void getPacks_allInMidx_midxEnabled_onlyMidx() throws IOException {
+		db.getObjectDatabase().setUseMultipackIndex(true);
+
+		DfsPackDescription gcPack = pack("aaaa", GC, 100, PACK);
+		DfsPackDescription compactPack = pack("cccc", COMPACT, 101, PACK);
+		DfsPackDescription compactTwoPack = pack("bbbb", COMPACT, 102, PACK);
+
+		DfsPackDescription multiPackIndex = pack("xxxx", GC, 104,
+				MULTI_PACK_INDEX);
+		multiPackIndex
+				.setCoveredPacks(List.of(gcPack, compactPack, compactTwoPack));
+
+		db.getObjectDatabase().commitPack(
+				List.of(gcPack, compactPack, compactTwoPack, multiPackIndex),
+				Collections.emptyList());
+
+		DfsPackFile[] packs = db.getObjectDatabase().getPacks();
+		assertPackList(packs, multiPackIndex);
+	}
+
+	@Test
+	public void getPacks_allInMidx_midxDisabled_packsNoMidx()
+			throws IOException {
+		db.getObjectDatabase().setUseMultipackIndex(false);
+
+		DfsPackDescription gcPack = pack("aaaa", GC, 100, PACK);
+		DfsPackDescription compactPack = pack("cccc", COMPACT, 101, PACK);
+		DfsPackDescription compactTwoPack = pack("bbbb", COMPACT, 102, PACK);
+
+		DfsPackDescription multiPackIndex = pack("xxxx", GC, 104,
+				MULTI_PACK_INDEX);
+		multiPackIndex
+				.setCoveredPacks(List.of(gcPack, compactPack, compactTwoPack));
+
+		db.getObjectDatabase().commitPack(
+				List.of(gcPack, compactPack, compactTwoPack, multiPackIndex),
+				Collections.emptyList());
+
+		DfsPackFile[] packs = db.getObjectDatabase().getPacks();
+		assertPackList(packs, compactTwoPack, compactPack, gcPack);
+	}
+
+    @Test
+	public void getPacks_someInMidx_midxEnabled_midxPlusUncovered()
+			throws IOException {
+		db.getObjectDatabase().setUseMultipackIndex(true);
+
+		DfsPackDescription gcPack = pack("aaaa", GC, 100, PACK);
+        DfsPackDescription compactPack = pack("cccc", COMPACT, 101, PACK);
+		DfsPackDescription insertPack = pack("bbbb", COMPACT, 102, PACK);
+
+        DfsPackDescription multiPackIndex = pack("xxxx", GC, 104,
+                MULTI_PACK_INDEX);
+        multiPackIndex.setCoveredPacks(List.of(gcPack, compactPack, insertPack));
+        db.getObjectDatabase().commitPack(
+                List.of(gcPack, compactPack, insertPack, multiPackIndex),
+                null);
+
+		DfsPackDescription uncoveredPack = pack("dddd", COMPACT, 103, PACK);
+		db.getObjectDatabase().commitPack(List.of(uncoveredPack), null);
+
+		DfsPackFile[] packs = db.getObjectDatabase().getPacks();
+		assertPackList(packs, uncoveredPack, multiPackIndex);
+    }
+
+	@Test
+	public void getPacks_someInMidx_midxDisabled_packsNoMidx()
+			throws IOException {
+		db.getObjectDatabase().setUseMultipackIndex(false);
+
+		DfsPackDescription gcPack = pack("aaaa", GC, 100, PACK);
+		DfsPackDescription compactPack = pack("cccc", COMPACT, 101, PACK);
+		DfsPackDescription insertPack = pack("bbbb", COMPACT, 102, PACK);
+
+		DfsPackDescription multiPackIndex = pack("xxxx", GC, 104,
+				MULTI_PACK_INDEX);
+		multiPackIndex
+				.setCoveredPacks(List.of(gcPack, compactPack, insertPack));
+		db.getObjectDatabase().commitPack(
+				List.of(gcPack, compactPack, insertPack, multiPackIndex), null);
+
+		DfsPackDescription uncoveredPack = pack("dddd", COMPACT, 103, PACK);
+		db.getObjectDatabase().commitPack(List.of(uncoveredPack), null);
+
+		DfsPackFile[] packs = db.getObjectDatabase().getPacks();
+		assertPackList(packs, uncoveredPack, insertPack, compactPack, gcPack);
+	}
+
+    @Test
+	public void getPacks_midxChain_midxEnabled_topMidx() throws IOException {
+		db.getObjectDatabase().setUseMultipackIndex(true);
+
+		DfsPackDescription gcPack = pack("aaaa", GC, 100, PACK);
+        DfsPackDescription compactPack = pack("cccc", COMPACT, 101, PACK);
+		DfsPackDescription insertPack = pack("bbbb", COMPACT, 102, PACK);
+
+        DfsPackDescription midxBase = pack("xxxx", GC, 104,
+                MULTI_PACK_INDEX);
+        midxBase.setCoveredPacks(List.of(gcPack, compactPack, insertPack));
+        db.getObjectDatabase().commitPack(
+                List.of(gcPack, compactPack, insertPack, midxBase),
+                null);
+
+        DfsPackDescription insert1 = pack("insert1", INSERT, 105, PACK);
+        DfsPackDescription insert2 = pack("insert2", INSERT, 106, PACK);
+        DfsPackDescription midxTip = pack("xxx2", GC, 107, MULTI_PACK_INDEX);
+        midxTip.setCoveredPacks(List.of(insert1, insert2));
+        midxTip.setMultiPackIndexBase(midxBase);
+        db.getObjectDatabase().commitPack(List.of(insert1, insert2, midxTip), null);
+
+        DfsPackFile[] packs = db.getObjectDatabase().getPacks();
+		assertPackList(packs, midxTip);
+    }
+
+	@Test
+	public void getPacks_midxChain_midxDisabled_packsNoMidx()
+			throws IOException {
+		db.getObjectDatabase().setUseMultipackIndex(false);
+
+		DfsPackDescription gcPack = pack("aaaa", GC, 100, PACK);
+		DfsPackDescription compactPack = pack("cccc", COMPACT, 101, PACK);
+		DfsPackDescription insertPack = pack("bbbb", COMPACT, 102, PACK);
+
+		DfsPackDescription midxBase = pack("xxxx", GC, 104, MULTI_PACK_INDEX);
+		midxBase.setCoveredPacks(List.of(gcPack, compactPack, insertPack));
+		db.getObjectDatabase().commitPack(
+				List.of(gcPack, compactPack, insertPack, midxBase), null);
+
+		DfsPackDescription insert1 = pack("insert1", INSERT, 105, PACK);
+		DfsPackDescription insert2 = pack("insert2", INSERT, 106, PACK);
+		DfsPackDescription midxTip = pack("xxx2", GC, 107, MULTI_PACK_INDEX);
+		midxTip.setCoveredPacks(List.of(insert1, insert2));
+		midxTip.setMultiPackIndexBase(midxBase);
+		db.getObjectDatabase().commitPack(List.of(insert1, insert2, midxTip),
+				null);
+
+		DfsPackFile[] packs = db.getObjectDatabase().getPacks();
+		assertPackList(packs, insert2, insert1, insertPack, compactPack,
+				gcPack);
+	}
+
+	@Test
+	public void getReftables_multipleInsideMidx() throws IOException {
+		db.getObjectDatabase().setUseMultipackIndex(true);
+
+		DfsPackDescription gcPack = pack("aaaa", GC, 100, PACK, REFTABLE);
+		DfsPackDescription compactPack = pack("cccc", COMPACT, 101, PACK,
+				REFTABLE);
+		DfsPackDescription compactTwoPack = pack("bbbb", COMPACT, 102, PACK);
+
+		DfsPackDescription multiPackIndex = pack("xxxx", GC, 104,
+				MULTI_PACK_INDEX);
+		multiPackIndex
+				.setCoveredPacks(List.of(gcPack, compactPack, compactTwoPack));
+
+		db.getObjectDatabase().commitPack(
+				List.of(gcPack, compactPack, compactTwoPack, multiPackIndex),
+				Collections.emptyList());
+
+		DfsReftable[] reftables = db.getObjectDatabase().getReftables();
+		assertReftableList(reftables, gcPack, compactPack);
+	}
+
+	@Test
+	public void getReftables_midxChain() throws IOException {
+		db.getObjectDatabase().setUseMultipackIndex(true);
+
+		DfsPackDescription gcPack = pack("aaaa", GC, 100, PACK, REFTABLE);
+		DfsPackDescription compactPack = pack("cccc", COMPACT, 101, PACK,
+				REFTABLE);
+		DfsPackDescription insertPack = pack("bbbb", COMPACT, 102, PACK);
+
+		DfsPackDescription midxBase = pack("xxxx", GC, 104, MULTI_PACK_INDEX);
+		midxBase.setCoveredPacks(List.of(gcPack, compactPack, insertPack));
+		db.getObjectDatabase().commitPack(
+				List.of(gcPack, compactPack, insertPack, midxBase), null);
+
+		DfsPackDescription insert1 = pack("insert1", INSERT, 105, PACK,
+				REFTABLE);
+		DfsPackDescription insert2 = pack("insert2", INSERT, 106, PACK);
+		DfsPackDescription midxTip = pack("xxx2", GC, 107, MULTI_PACK_INDEX);
+		midxTip.setCoveredPacks(List.of(insert1, insert2));
+		midxTip.setMultiPackIndexBase(midxBase);
+		db.getObjectDatabase().commitPack(List.of(insert1, insert2, midxTip),
+				null);
+
+		DfsReftable[] reftables = db.getObjectDatabase().getReftables();
+		assertReftableList(reftables, gcPack, compactPack, insert1);
+	}
+
+	@Test
+	public void getReftables_inAndOutOfMidx() throws IOException {
+		db.getObjectDatabase().setUseMultipackIndex(true);
+
+		DfsPackDescription gcPack = pack("aaaa", GC, 100, PACK, REFTABLE);
+		DfsPackDescription compactPack = pack("cccc", COMPACT, 101, PACK);
+		DfsPackDescription insertPack = pack("bbbb", COMPACT, 102, PACK);
+
+		DfsPackDescription multiPackIndex = pack("xxxx", GC, 104,
+				MULTI_PACK_INDEX);
+		multiPackIndex
+				.setCoveredPacks(List.of(gcPack, compactPack, insertPack));
+		db.getObjectDatabase().commitPack(
+				List.of(gcPack, compactPack, insertPack, multiPackIndex), null);
+
+		DfsPackDescription uncoveredPack = pack("dddd", COMPACT, 103, PACK,
+				REFTABLE);
+		db.getObjectDatabase().commitPack(List.of(uncoveredPack), null);
+
+		DfsReftable[] reftables = db.getObjectDatabase().getReftables();
+		assertReftableList(reftables, gcPack, uncoveredPack);
+	}
+
+	private static DfsPackDescription pack(String name,
+			DfsObjDatabase.PackSource source, long timeMs, PackExt... ext) {
+		DfsPackDescription desc = new DfsPackDescription(repoDesc, name,
+				source);
+		desc.setLastModified(timeMs);
+		for (PackExt packExt : ext) {
+			desc.addFileExt(packExt);
+		}
+		return desc;
+	}
+
+	private static void assertPackList(DfsPackFile[] actual,
+			DfsPackDescription... expected) {
+		assertEquals(expected.length, actual.length);
+		for (int i = 0; i < expected.length; i++) {
+			assertEquals(expected[i], actual[i].getPackDescription());
+		}
+	}
+
+	private static void assertReftableList(DfsReftable[] actual,
+			DfsPackDescription... expected) {
+		assertEquals(expected.length, actual.length);
+		for (int i = 0; i < expected.length; i++) {
+			assertEquals(expected[i], actual[i].getPackDescription());
+		}
+	}
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackCompacterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackCompacterTest.java
index c3b6aa8..84505a8 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackCompacterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackCompacterTest.java
@@ -14,6 +14,7 @@
 import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.INSERT;
 import static org.eclipse.jgit.internal.storage.pack.PackExt.OBJECT_SIZE_INDEX;
 import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
+import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -21,14 +22,20 @@
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.Optional;
+import java.util.zip.Deflater;
 
+import org.eclipse.jgit.junit.JGitTestUtil;
 import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.junit.TestRng;
 import org.eclipse.jgit.lib.ConfigConstants;
+import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.junit.Before;
 import org.junit.Test;
 
 public class DfsPackCompacterTest {
+	private static final int AUTO_ADD_SIZE = 5 * 1024 * 1024; // 5 MiB
+
 	private TestRepository<InMemoryRepository> git;
 	private InMemoryRepository repo;
 	private DfsObjDatabase odb;
@@ -137,19 +144,62 @@ public void testObjectSizeIndexNotWritten() throws Exception {
 		assertFalse(compactPack.get().getPackDescription().hasFileExt(OBJECT_SIZE_INDEX));
 	}
 
+	@Test
+	public void testPrunePack() throws Exception {
+		ObjectId o1 = writePackWithRandomBlob(200);
+		ObjectId o2 = writePackWithRandomBlob(100);
+		ObjectId o3 = writePackWithRandomBlob(210);
+		DfsPackFile[] packsToCompact = odb.getPacks();
+
+		DfsPackCompactor compactor = new DfsPackCompactor(repo);
+		compactor.add(packsToCompact[1]);
+		compactor.add(packsToCompact[2]);
+		compactor.prune(packsToCompact[0]);
+
+		compactor.compact(null);
+
+		assertEquals(1, odb.getPacks().length);
+		assertFalse(odb.has(o3)); // pack with o3 was pruned
+		assertTrue(odb.has(o1));
+		assertTrue(odb.has(o2));
+	}
+
 	private TestRepository<InMemoryRepository>.CommitBuilder commit() {
 		return git.commit();
 	}
 
 	private void compact() throws IOException {
 		DfsPackCompactor compactor = new DfsPackCompactor(repo);
-		compactor.autoAdd();
+		DfsObjDatabase objdb = repo.getObjectDatabase();
+		for (DfsPackFile pack : objdb.getPacks()) {
+			DfsPackDescription d = pack.getPackDescription();
+			if (d.getFileSize(PACK) < AUTO_ADD_SIZE) {
+				compactor.add(pack);
+			} else {
+				compactor.exclude(pack);
+			}
+		}
 		compactor.compact(null);
 		odb.clearCache();
 	}
 
 	private static void writeObjectSizeIndex(DfsRepository repo, boolean should) {
 		repo.getConfig().setInt(ConfigConstants.CONFIG_PACK_SECTION, null,
-				ConfigConstants.CONFIG_KEY_MIN_BYTES_OBJ_SIZE_INDEX, should ? 0 : -1);
+				ConfigConstants.CONFIG_KEY_MIN_BYTES_OBJ_SIZE_INDEX,
+				should ? 0 : -1);
+	}
+
+	private ObjectId writePackWithBlob(byte[] data) throws IOException {
+		DfsInserter ins = (DfsInserter) repo.newObjectInserter();
+		ins.setCompressionLevel(Deflater.NO_COMPRESSION);
+		ObjectId blobId = ins.insert(OBJ_BLOB, data);
+		ins.flush();
+		return blobId;
+	}
+
+	// Do not use the size twice into the same test (it gives the same blob!)
+	private ObjectId writePackWithRandomBlob(int size) throws IOException {
+		byte[] data = new TestRng(JGitTestUtil.getName()).nextBytes(size);
+		return writePackWithBlob(data);
 	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackFileMidxTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackFileMidxTest.java
new file mode 100644
index 0000000..3fd4e2c
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackFileMidxTest.java
@@ -0,0 +1,1267 @@
+/*
+ * Copyright (C) 2025, 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.dfs;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.GC;
+import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
+import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
+import static org.eclipse.jgit.lib.Constants.OBJ_COMMIT;
+import static org.eclipse.jgit.lib.Constants.OBJ_TREE;
+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.assertThrows;
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.zip.Deflater;
+
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.internal.storage.dfs.DfsPackFileMidx.VOffsetCalculator;
+import org.eclipse.jgit.internal.storage.file.PackBitmapIndex;
+import org.eclipse.jgit.internal.storage.file.PackIndex;
+import org.eclipse.jgit.internal.storage.midx.MultiPackIndex.PackOffset;
+import org.eclipse.jgit.internal.storage.midx.MultiPackIndexWriter;
+import org.eclipse.jgit.internal.storage.pack.ObjectToPack;
+import org.eclipse.jgit.internal.storage.pack.PackExt;
+import org.eclipse.jgit.internal.storage.pack.PackOutputStream;
+import org.eclipse.jgit.internal.storage.pack.PackWriter;
+import org.eclipse.jgit.junit.JGitTestUtil;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.junit.TestRng;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.ProgressMonitor;
+import org.eclipse.jgit.revwalk.RevBlob;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevTree;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.storage.pack.PackConfig;
+import org.junit.Before;
+import org.junit.Test;
+
+public class DfsPackFileMidxTest {
+
+	private static final ObjectId NOT_IN_PACK = ObjectId
+			.fromString("3f306cb3fcd5116919fecad615524bd6e6ea4ba7");
+
+	InMemoryRepository db;
+
+	@Before
+	public void setUp() {
+		db = new InMemoryRepository(new DfsRepositoryDescription("test"));
+	}
+
+	@Test
+	public void midx_findIdxPosition() throws IOException {
+		ObjectId o1 = writePackWithBlob("something".getBytes(UTF_8));
+		ObjectId o2 = writePackWithBlob("something else".getBytes(UTF_8));
+		ObjectId o3 = writePackWithBlob("and more".getBytes(UTF_8));
+		DfsPackFile midx = writeMultipackIndex();
+
+		try (DfsReader ctx = db.getObjectDatabase().newReader()) {
+			assertEquals(2, midx.findIdxPosition(ctx, o1));
+			assertEquals(0, midx.findIdxPosition(ctx, o2));
+			assertEquals(1, midx.findIdxPosition(ctx, o3));
+			assertEquals(-1, midx.findIdxPosition(ctx, NOT_IN_PACK));
+		}
+	}
+
+	@Test
+	public void midx_findIdxPosition_withBase() throws IOException {
+		ObjectId o1 = writePackWithBlob("o1".getBytes(UTF_8));
+		ObjectId o2 = writePackWithBlob("o2".getBytes(UTF_8));
+		ObjectId o3 = writePackWithBlob("o3".getBytes(UTF_8));
+		ObjectId o4 = writePackWithBlob("o4".getBytes(UTF_8));
+		ObjectId o5 = writePackWithBlob("o5".getBytes(UTF_8));
+		ObjectId o6 = writePackWithBlob("o6".getBytes(UTF_8));
+		DfsPackFile[] packs = db.getObjectDatabase().getPacks();
+
+		// Packs are in reverse insertion order
+		DfsPackFileMidx midxBase = writeMultipackIndex(
+				Arrays.copyOfRange(packs, 4, 6), null);
+		DfsPackFileMidx midxMid = writeMultipackIndex(
+				Arrays.copyOfRange(packs, 2, 4), midxBase);
+		DfsPackFileMidx midxTip = writeMultipackIndex(
+				Arrays.copyOfRange(packs, 0, 2), midxMid);
+
+		try (DfsReader ctx = db.getObjectDatabase().newReader()) {
+			assertEquals(0, midxTip.findIdxPosition(ctx, o1));
+			assertEquals(1, midxTip.findIdxPosition(ctx, o2));
+			assertEquals(2, midxTip.findIdxPosition(ctx, o3));
+			assertEquals(3, midxTip.findIdxPosition(ctx, o4));
+			// In sha1 order
+			assertEquals(5, midxTip.findIdxPosition(ctx, o5));
+			assertEquals(4, midxTip.findIdxPosition(ctx, o6));
+		}
+	}
+
+	@Test
+	public void midx_hasObject() throws IOException {
+		ObjectId o1 = writePackWithRandomBlob(100);
+		ObjectId o2 = writePackWithRandomBlob(200);
+		ObjectId o3 = writePackWithRandomBlob(150);
+		DfsPackFile midx = writeMultipackIndex();
+
+		// DfsPackFile midx = readDfsPackFileMidx();
+		try (DfsReader ctx = db.getObjectDatabase().newReader()) {
+			assertTrue(midx.hasObject(ctx, o1));
+			assertTrue(midx.hasObject(ctx, o2));
+			assertTrue(midx.hasObject(ctx, o3));
+			assertFalse(midx.hasObject(ctx, NOT_IN_PACK));
+		}
+	}
+
+	@Test
+	public void midx_hasObject_withBase() throws IOException {
+		ObjectId o1 = writePackWithRandomBlob(100);
+		ObjectId o2 = writePackWithRandomBlob(200);
+		ObjectId o3 = writePackWithRandomBlob(150);
+		ObjectId o4 = writePackWithRandomBlob(400);
+		ObjectId o5 = writePackWithRandomBlob(500);
+		ObjectId o6 = writePackWithRandomBlob(600);
+		DfsPackFile[] packs = db.getObjectDatabase().getPacks();
+
+		// Packs are in reverse insertion order
+		DfsPackFileMidx midxBase = writeMultipackIndex(
+				Arrays.copyOfRange(packs, 3, 6), null);
+		DfsPackFileMidx midxTip = writeMultipackIndex(
+				Arrays.copyOfRange(packs, 0, 3), midxBase);
+
+		try (DfsReader ctx = db.getObjectDatabase().newReader()) {
+			assertTrue(midxBase.hasObject(ctx, o1));
+			assertTrue(midxBase.hasObject(ctx, o2));
+			assertTrue(midxBase.hasObject(ctx, o3));
+			// These are not in base
+			assertFalse(midxBase.hasObject(ctx, o4));
+			assertFalse(midxBase.hasObject(ctx, o5));
+			assertFalse(midxBase.hasObject(ctx, o6));
+
+			// Top midx has all objects
+			assertTrue(midxTip.hasObject(ctx, o1));
+			assertTrue(midxTip.hasObject(ctx, o2));
+			assertTrue(midxTip.hasObject(ctx, o3));
+			assertTrue(midxTip.hasObject(ctx, o4));
+			assertTrue(midxTip.hasObject(ctx, o5));
+			assertTrue(midxTip.hasObject(ctx, o6));
+		}
+	}
+
+	@Test
+	public void midx_get() throws IOException {
+		byte[] contentOne = "ONE".getBytes(UTF_8);
+		byte[] contentTwo = "TWO".getBytes(UTF_8);
+		byte[] contentThree = "THREE".getBytes(UTF_8);
+
+		ObjectId o1 = writePackWithBlob(contentOne);
+		ObjectId o2 = writePackWithBlob(contentTwo);
+		ObjectId o3 = writePackWithBlob(contentThree);
+		DfsPackFile midx = writeMultipackIndex();
+
+		try (DfsReader ctx = db.getObjectDatabase().newReader()) {
+			ObjectLoader objectLoader = midx.get(ctx, o1);
+			assertArrayEquals(contentOne, safeGetBytes(objectLoader));
+			objectLoader = midx.get(ctx, o2);
+			assertArrayEquals(contentTwo, safeGetBytes(objectLoader));
+			objectLoader = midx.get(ctx, o3);
+			assertArrayEquals(contentThree, safeGetBytes(objectLoader));
+			assertNull(midx.get(ctx, NOT_IN_PACK));
+		}
+	}
+
+	@Test
+	public void midx_get_withBase() throws IOException {
+		byte[] contentOne = "ONE".getBytes(UTF_8);
+		byte[] contentTwo = "TWO".getBytes(UTF_8);
+		byte[] contentThree = "THREE".getBytes(UTF_8);
+		byte[] contentFour = "FOUR".getBytes(UTF_8);
+		byte[] contentFive = "FIVE".getBytes(UTF_8);
+		byte[] contentSix = "SIX".getBytes(UTF_8);
+
+		ObjectId o1 = writePackWithBlob(contentOne);
+		ObjectId o2 = writePackWithBlob(contentTwo);
+		ObjectId o3 = writePackWithBlob(contentThree);
+		ObjectId o4 = writePackWithBlob(contentFour);
+		ObjectId o5 = writePackWithBlob(contentFive);
+		ObjectId o6 = writePackWithBlob(contentSix);
+
+		DfsPackFile[] packs = db.getObjectDatabase().getPacks();
+		// Packs are in reverse insertion order
+		DfsPackFileMidx midxBase = writeMultipackIndex(
+				Arrays.copyOfRange(packs, 3, 6), null);
+		DfsPackFileMidx midxTip = writeMultipackIndex(
+				Arrays.copyOfRange(packs, 0, 3), midxBase);
+
+		try (DfsReader ctx = db.getObjectDatabase().newReader()) {
+			ObjectLoader objectLoader = midxTip.get(ctx, o1);
+			assertArrayEquals(contentOne, safeGetBytes(objectLoader));
+			objectLoader = midxTip.get(ctx, o2);
+			assertArrayEquals(contentTwo, safeGetBytes(objectLoader));
+			objectLoader = midxTip.get(ctx, o3);
+			assertArrayEquals(contentThree, safeGetBytes(objectLoader));
+			objectLoader = midxTip.get(ctx, o4);
+			assertArrayEquals(contentFour, safeGetBytes(objectLoader));
+			objectLoader = midxTip.get(ctx, o5);
+			assertArrayEquals(contentFive, safeGetBytes(objectLoader));
+			objectLoader = midxTip.get(ctx, o6);
+			assertArrayEquals(contentSix, safeGetBytes(objectLoader));
+
+			assertNull(midxTip.get(ctx, NOT_IN_PACK));
+		}
+	}
+
+	@Test
+	public void midx_load() throws IOException {
+		byte[] contentOne = "ONE".getBytes(UTF_8);
+		byte[] contentTwo = "TWO".getBytes(UTF_8);
+		byte[] contentThree = "THREE".getBytes(UTF_8);
+
+		ObjectId o1 = writePackWithBlob(contentOne);
+		ObjectId o2 = writePackWithBlob(contentTwo);
+		ObjectId o3 = writePackWithBlob(contentThree);
+		DfsPackFile midx = writeMultipackIndex();
+
+		try (DfsReader ctx = db.getObjectDatabase().newReader()) {
+			ObjectLoader objectLoader = midx.load(ctx,
+					midx.findOffset(ctx, o1));
+			assertArrayEquals(contentOne, safeGetBytes(objectLoader));
+			objectLoader = midx.load(ctx, midx.findOffset(ctx, o2));
+			assertArrayEquals(contentTwo, safeGetBytes(objectLoader));
+			objectLoader = midx.load(ctx, midx.findOffset(ctx, o3));
+			assertArrayEquals(contentThree, safeGetBytes(objectLoader));
+
+			assertThrows(IllegalArgumentException.class,
+					() -> midx.load(ctx, 500));
+			assertNull(midx.load(ctx, -1));
+		}
+	}
+
+	@Test
+	public void midx_load_withBase() throws IOException {
+		byte[] contentOne = "ONE".getBytes(UTF_8);
+		byte[] contentTwo = "TWO".getBytes(UTF_8);
+		byte[] contentThree = "THREE".getBytes(UTF_8);
+		byte[] contentFour = "FOUR".getBytes(UTF_8);
+		byte[] contentFive = "FIVE".getBytes(UTF_8);
+		byte[] contentSix = "SIX".getBytes(UTF_8);
+
+		ObjectId o1 = writePackWithBlob(contentOne);
+		ObjectId o2 = writePackWithBlob(contentTwo);
+		ObjectId o3 = writePackWithBlob(contentThree);
+		ObjectId o4 = writePackWithBlob(contentFour);
+		ObjectId o5 = writePackWithBlob(contentFive);
+		ObjectId o6 = writePackWithBlob(contentSix);
+
+		DfsPackFile[] packs = db.getObjectDatabase().getPacks();
+		// Packs are in reverse insertion order
+		DfsPackFileMidx midxBase = writeMultipackIndex(
+				Arrays.copyOfRange(packs, 3, 6), null);
+		DfsPackFileMidx midxTip = writeMultipackIndex(
+				Arrays.copyOfRange(packs, 0, 3), midxBase);
+
+		try (DfsReader ctx = db.getObjectDatabase().newReader()) {
+			ObjectLoader objectLoader = midxTip.load(ctx,
+					midxTip.findOffset(ctx, o1));
+			assertArrayEquals(contentOne, safeGetBytes(objectLoader));
+			objectLoader = midxTip.load(ctx, midxTip.findOffset(ctx, o2));
+			assertArrayEquals(contentTwo, safeGetBytes(objectLoader));
+			objectLoader = midxTip.load(ctx, midxTip.findOffset(ctx, o3));
+			assertArrayEquals(contentThree, safeGetBytes(objectLoader));
+			objectLoader = midxTip.load(ctx, midxTip.findOffset(ctx, o4));
+			assertArrayEquals(contentFour, safeGetBytes(objectLoader));
+			objectLoader = midxTip.load(ctx, midxTip.findOffset(ctx, o5));
+			assertArrayEquals(contentFive, safeGetBytes(objectLoader));
+			objectLoader = midxTip.load(ctx, midxTip.findOffset(ctx, o6));
+			assertArrayEquals(contentSix, safeGetBytes(objectLoader));
+
+			assertNull(midxTip.load(ctx, midxTip.findOffset(ctx, NOT_IN_PACK)));
+		}
+	}
+
+	@Test
+	public void midx_findOffset() throws IOException {
+		ObjectId o1 = writePackWithRandomBlob(100);
+		ObjectId o2 = writePackWithRandomBlob(200);
+		ObjectId o3 = writePackWithRandomBlob(150);
+		// Packs are written in the midx in reverse insertion time
+		DfsPackFileMidx midx = writeMultipackIndex();
+
+		DfsPackFile packOne = findPack(o1);
+		DfsPackFile packTwo = findPack(o2);
+		long packTwoSize = packTwo.getPackDescription().getFileSize(PACK);
+		DfsPackFile packThree = findPack(o3);
+		long packThreeSize = packThree.getPackDescription().getFileSize(PACK);
+
+		try (DfsReader ctx = db.getObjectDatabase().newReader()) {
+			long posOne = midx.findOffset(ctx, o1);
+			DfsPackFileMidx.DfsPackOffset po = midx.getOffsetCalculator()
+					.decode(posOne);
+			assertEquals(12, po.getPackOffset());
+			assertEquals(packThreeSize + packTwoSize, po.getPackStart());
+			assertEquals(packOne.getPackDescription(),
+					po.getPack().getPackDescription());
+
+			long posTwo = midx.findOffset(ctx, o2);
+			po = midx.getOffsetCalculator().decode(posTwo);
+			assertEquals(12, po.getPackOffset());
+			assertEquals(packThreeSize, po.getPackStart());
+			assertEquals(packTwo.getPackDescription(),
+					po.getPack().getPackDescription());
+
+			long posThree = midx.findOffset(ctx, o3);
+			po = midx.getOffsetCalculator().decode(posThree);
+			assertEquals(12, po.getPackOffset());
+			assertEquals(0, po.getPackStart());
+			assertEquals(packThree.getPackDescription(),
+					po.getPack().getPackDescription());
+
+			long posNon = midx.findOffset(ctx, NOT_IN_PACK);
+			assertEquals(-1, posNon);
+		}
+	}
+
+	@Test
+	public void midx_resolve() throws Exception {
+		ObjectId o1 = writePackWithRandomBlob(100);
+		writePackWithRandomBlob(200);
+		ObjectId o2 = writePackWithRandomBlob(150);
+		DfsPackFile midx = writeMultipackIndex();
+
+		try (DfsReader ctx = db.getObjectDatabase().newReader()) {
+			Set<ObjectId> matches = new HashSet<>();
+			midx.resolve(ctx, matches, o1.abbreviate(6), 100);
+			assertEquals(1, matches.size());
+
+			matches.clear();
+			midx.resolve(ctx, matches, o2.abbreviate(6), 100);
+			assertEquals(1, matches.size());
+
+			matches = new HashSet<>();
+			midx.resolve(ctx, matches, NOT_IN_PACK.abbreviate(8), 100);
+			assertEquals(0, matches.size());
+		}
+	}
+
+	@Test
+	public void midx_resolve_withBase() throws Exception {
+		ObjectId o1 = writePackWithRandomBlob(100);
+		ObjectId o2 = writePackWithRandomBlob(200);
+		writePackWithRandomBlob(150);
+		ObjectId o4 = writePackWithRandomBlob(400);
+		writePackWithRandomBlob(500);
+		writePackWithRandomBlob(600);
+
+		DfsPackFile[] packs = db.getObjectDatabase().getPacks();
+		// Packs are in reverse insertion order
+		DfsPackFileMidx midxBase = writeMultipackIndex(
+				Arrays.copyOfRange(packs, 3, 6), null);
+		DfsPackFileMidx midxTip = writeMultipackIndex(
+				Arrays.copyOfRange(packs, 0, 3), midxBase);
+
+		try (DfsReader ctx = db.getObjectDatabase().newReader()) {
+			Set<ObjectId> matches = new HashSet<>();
+			// Test with an ID that exists in the base
+			assertTrue(midxTip.hasObject(ctx, o1));
+			midxTip.resolve(ctx, matches, o1.abbreviate(6), 100);
+			assertEquals(1, matches.size());
+			assertTrue(matches.contains(o1));
+
+			matches.clear();
+			// Test with an ID that exists in the tip
+			midxTip.resolve(ctx, matches, o4.abbreviate(6), 100);
+			assertTrue(matches.contains(o4));
+			assertEquals(1, matches.size());
+
+			matches.clear();
+			midxTip.resolve(ctx, matches, o2.abbreviate(6), 1);
+			assertTrue(matches.contains(o2));
+			assertEquals(1, matches.size());
+
+			matches.clear();
+			midxTip.resolve(ctx, matches, NOT_IN_PACK.abbreviate(8), 100);
+			assertEquals(0, matches.size());
+		}
+	}
+
+	@Test
+	public void midx_findAllFromPack() throws Exception {
+		ObjectId o1 = writePackWithRandomBlob(100);
+		ObjectId o2 = writePackWithRandomBlob(200);
+		ObjectId o3 = writePackWithRandomBlob(150);
+		DfsPackFile midx = writeMultipackIndex();
+
+		List<ObjectToPack> otps = List.of(new DfsObjectToPack(o1, OBJ_BLOB),
+				new DfsObjectToPack(o2, OBJ_BLOB),
+				new DfsObjectToPack(o3, OBJ_BLOB),
+				new DfsObjectToPack(NOT_IN_PACK, OBJ_BLOB));
+
+		try (DfsReader ctx = db.getObjectDatabase().newReader()) {
+			List<DfsObjectToPack> allFromPack = midx.findAllFromPack(ctx, otps,
+					true);
+			assertEquals(3, allFromPack.size());
+
+			// Objects are in (pack, offset) order (i.e. reverse pack insert)
+			DfsObjectToPack oneToPack = allFromPack.get(0);
+			assertEquals(midx.findOffset(ctx, o3), oneToPack.getOffset());
+
+			DfsObjectToPack twoToPack = allFromPack.get(1);
+			assertEquals(midx.findOffset(ctx, o2), twoToPack.getOffset());
+
+			DfsObjectToPack threeToPack = allFromPack.get(2);
+			assertEquals(midx.findOffset(ctx, o1), threeToPack.getOffset());
+		}
+	}
+
+	@Test
+	public void midx_findAllFromPack_withBase() throws Exception {
+		ObjectId o1 = writePackWithRandomBlob(100);
+		ObjectId o2 = writePackWithRandomBlob(200);
+		ObjectId o3 = writePackWithRandomBlob(150);
+		ObjectId o4 = writePackWithRandomBlob(400);
+		ObjectId o5 = writePackWithRandomBlob(500);
+		ObjectId o6 = writePackWithRandomBlob(600);
+
+		DfsPackFile[] packs = db.getObjectDatabase().getPacks();
+		// Packs are in reverse insertion order (o6 -> o1)
+		DfsPackFileMidx midxBase = writeMultipackIndex(
+				Arrays.copyOfRange(packs, 3, 6), null);
+		DfsPackFileMidx midxTip = writeMultipackIndex(
+				Arrays.copyOfRange(packs, 0, 3), midxBase);
+
+		List<ObjectToPack> otps = List.of(new DfsObjectToPack(o1, OBJ_BLOB),
+				new DfsObjectToPack(o4, OBJ_BLOB),
+				new DfsObjectToPack(o2, OBJ_BLOB),
+				new DfsObjectToPack(o5, OBJ_BLOB),
+				new DfsObjectToPack(NOT_IN_PACK, OBJ_BLOB),
+				new DfsObjectToPack(o3, OBJ_BLOB),
+				new DfsObjectToPack(o6, OBJ_BLOB));
+
+		try (DfsReader ctx = db.getObjectDatabase().newReader()) {
+			List<DfsObjectToPack> allFromPack = midxTip.findAllFromPack(ctx,
+					otps, true);
+			assertEquals(6, allFromPack.size());
+
+			// Objects are in midx-offset order which is:
+			// base(o3 < o2 < o1) -> tip (06 < 05 < o4)
+			DfsObjectToPack oneToPack = allFromPack.get(0);
+			assertEquals(midxTip.findOffset(ctx, o3), oneToPack.getOffset());
+
+			DfsObjectToPack twoToPack = allFromPack.get(1);
+			assertEquals(midxTip.findOffset(ctx, o2), twoToPack.getOffset());
+
+			DfsObjectToPack threeToPack = allFromPack.get(2);
+			assertEquals(midxTip.findOffset(ctx, o1), threeToPack.getOffset());
+
+			DfsObjectToPack fourToPack = allFromPack.get(3);
+			assertEquals(midxTip.findOffset(ctx, o6), fourToPack.getOffset());
+
+			DfsObjectToPack fiveToPack = allFromPack.get(4);
+			assertEquals(midxTip.findOffset(ctx, o5), fiveToPack.getOffset());
+
+			DfsObjectToPack sixToPack = allFromPack.get(5);
+			assertEquals(midxTip.findOffset(ctx, o4), sixToPack.getOffset());
+		}
+	}
+
+	@Test
+	public void midx_copyPackAsIs() throws Exception {
+		writePackWithRandomBlob(100);
+		writePackWithRandomBlob(200);
+		writePackWithRandomBlob(150);
+
+		long expectedPackSize = Arrays.stream(db.getObjectDatabase().getPacks())
+				.mapToLong(pack -> pack.getPackDescription().getFileSize(PACK))
+				.map(size -> size - 12 - 20) // remove header + CRC
+				.sum();
+		DfsPackFile midx = writeMultipackIndex();
+
+		try (DfsReader ctx = db.getObjectDatabase().newReader();
+				PackWriter pw = new PackWriter(new PackConfig(), ctx);
+				ByteArrayOutputStream os = new ByteArrayOutputStream();
+				PackOutputStream out = new PackOutputStream(
+						NullProgressMonitor.INSTANCE, os, pw)) {
+			midx.copyPackAsIs(out, ctx);
+			out.flush();
+			assertEquals(expectedPackSize, os.size());
+		}
+	}
+
+	@Test
+	public void midx_copyPackAsIs_withBase() throws Exception {
+		writePackWithRandomBlob(100);
+		writePackWithRandomBlob(200);
+		writePackWithRandomBlob(150);
+		writePackWithRandomBlob(400);
+		writePackWithRandomBlob(500);
+		writePackWithRandomBlob(600);
+
+		DfsPackFile[] packs = db.getObjectDatabase().getPacks();
+		long expectedPackSize = Arrays.stream(packs)
+				.mapToLong(pack -> pack.getPackDescription().getFileSize(PACK))
+				.map(size -> size - 12 - 20) // remove header + CRC
+				.sum();
+
+		DfsPackFileMidx midxBase = writeMultipackIndex(
+				Arrays.copyOfRange(packs, 3, 6), null);
+		DfsPackFileMidx midxTip = writeMultipackIndex(
+				Arrays.copyOfRange(packs, 0, 3), midxBase);
+
+		try (DfsReader ctx = db.getObjectDatabase().newReader();
+				PackWriter pw = new PackWriter(new PackConfig(), ctx);
+				ByteArrayOutputStream os = new ByteArrayOutputStream();
+				PackOutputStream out = new PackOutputStream(
+						NullProgressMonitor.INSTANCE, os, pw)) {
+			midxTip.copyPackAsIs(out, ctx);
+			out.flush();
+			assertEquals(expectedPackSize, os.size());
+		}
+	}
+
+	@Test
+	public void midx_copyAsIs() throws Exception {
+		writePackWithRandomBlob(100);
+		ObjectId blob = writePackWithRandomBlob(200);
+		writePackWithRandomBlob(150);
+		DfsPackFileMidx midx = writeMultipackIndex();
+
+		assertEquals(213, copyAsIs(midx, blob).length);
+	}
+
+	@Test
+	public void midx_copyAsIs_withBase() throws Exception {
+		writePackWithRandomBlob(100);
+		ObjectId baseObject = writePackWithRandomBlob(200);
+		writePackWithRandomBlob(150);
+		writePackWithRandomBlob(400);
+		ObjectId tipObject = writePackWithRandomBlob(500);
+		writePackWithRandomBlob(600);
+
+		DfsPackFile[] packs = db.getObjectDatabase().getPacks();
+		// Packs are in reverse insertion order
+		DfsPackFileMidx midxBase = writeMultipackIndex(
+				Arrays.copyOfRange(packs, 3, 6), null);
+		DfsPackFileMidx midxTip = writeMultipackIndex(
+				Arrays.copyOfRange(packs, 0, 3), midxBase);
+
+		assertEquals(213, copyAsIs(midxTip, baseObject).length);
+		assertEquals(513, copyAsIs(midxTip, tipObject).length);
+	}
+
+	private byte[] copyAsIs(DfsPackFileMidx midx, ObjectId oid)
+			throws Exception {
+		try (DfsReader ctx = db.getObjectDatabase().newReader();
+				PackWriter pw = new PackWriter(new PackConfig(), ctx);
+				ByteArrayOutputStream os = new ByteArrayOutputStream();
+				PackOutputStream out = new PackOutputStream(
+						NullProgressMonitor.INSTANCE, os, pw)) {
+			// Object in the base
+			ObjectToPack otp = new DfsObjectToPack(oid, OBJ_BLOB);
+			DfsObjectToPack inPack = midx
+					.findAllFromPack(ctx, List.of(otp), false).get(0);
+			DfsObjectRepresentation r = new DfsObjectRepresentation(midx);
+			midx.fillRepresentation(r, inPack.getOffset(), ctx);
+			inPack.select(r);
+			midx.copyAsIs(out, inPack, false, ctx);
+			out.flush();
+			return os.toByteArray();
+		}
+	}
+
+	@Test
+	public void midx_getDeltaHeader() {
+		// TODO(ifrade): Implement
+	}
+
+	@Test
+	public void midx_getObjectType() throws Exception {
+		ObjectId commit = writePackWithCommit();
+		ObjectId blob = writePackWithRandomBlob(200);
+		DfsPackFile midx = writeMultipackIndex();
+
+		try (RevWalk rw = new RevWalk(db);
+				DfsReader ctx = db.getObjectDatabase().newReader()) {
+			RevCommit aCommit = rw.parseCommit(commit);
+			RevTree aTree = rw.parseTree(aCommit.getTree());
+
+			long commitPos = midx.findOffset(ctx, commit);
+			assertEquals(OBJ_COMMIT, midx.getObjectType(ctx, commitPos));
+
+			long treePos = midx.findOffset(ctx, aTree);
+			assertEquals(OBJ_TREE, midx.getObjectType(ctx, treePos));
+
+			long blobPos = midx.findOffset(ctx, blob);
+			assertEquals(OBJ_BLOB, midx.getObjectType(ctx, blobPos));
+
+			assertThrows(IllegalArgumentException.class,
+					() -> midx.getObjectType(ctx, 12000));
+		}
+	}
+
+	@Test
+	public void midx_getObjectType_withBase() throws Exception {
+		// This first commit creates two packs
+		ObjectId commit = writePackWithCommit();
+		ObjectId blob = writePackWithRandomBlob(200);
+		writePackWithCommit();
+
+		writePackWithCommit();
+		writePackWithRandomBlob(300);
+		ObjectId newCommit = writePackWithCommit();
+
+		DfsPackFile[] packs = db.getObjectDatabase().getPacks();
+		// Packs are in reverse insertion order
+		DfsPackFileMidx midxBase = writeMultipackIndex(
+				Arrays.copyOfRange(packs, 4, 7), null);
+		DfsPackFileMidx midxTip = writeMultipackIndex(
+				Arrays.copyOfRange(packs, 0, 4), midxBase);
+
+		try (RevWalk rw = new RevWalk(db);
+				DfsReader ctx = db.getObjectDatabase().newReader()) {
+			RevCommit aCommit = rw.parseCommit(commit);
+			RevTree aTree = rw.parseTree(aCommit.getTree());
+
+			long commitPos = midxTip.findOffset(ctx, commit);
+			assertEquals(OBJ_COMMIT, midxTip.getObjectType(ctx, commitPos));
+
+			long treePos = midxTip.findOffset(ctx, aTree);
+			assertEquals(OBJ_TREE, midxTip.getObjectType(ctx, treePos));
+
+			long blobPos = midxTip.findOffset(ctx, blob);
+			assertEquals(OBJ_BLOB, midxTip.getObjectType(ctx, blobPos));
+
+			long commitPosTip = midxTip.findOffset(ctx, newCommit);
+			assertEquals(OBJ_COMMIT, midxTip.getObjectType(ctx, commitPosTip));
+
+			assertThrows(IllegalArgumentException.class,
+					() -> midxTip.getObjectType(ctx, 12000));
+		}
+	}
+
+	@Test
+	public void midx_getObjectSize_byId() throws Exception {
+		ObjectId commit = writePackWithCommit();
+		ObjectId blob = writePackWithRandomBlob(200);
+		DfsPackFile midx = writeMultipackIndex();
+
+		try (RevWalk rw = new RevWalk(db);
+				DfsReader ctx = db.getObjectDatabase().newReader()) {
+			RevCommit aCommit = rw.parseCommit(commit);
+			RevTree aTree = rw.parseTree(aCommit.getTree());
+			RevBlob aBlob = rw.lookupBlob(blob);
+
+			assertEquals(168, midx.getObjectSize(ctx, aCommit));
+			assertEquals(33, midx.getObjectSize(ctx, aTree));
+			assertEquals(200, midx.getObjectSize(ctx, aBlob));
+
+			assertEquals(-1, midx.getObjectSize(ctx, NOT_IN_PACK));
+		}
+	}
+
+	@Test
+	public void midx_getObjectSize_byId_withBase() throws Exception {
+		ObjectId commit = writePackWithCommit();
+		ObjectId blob = writePackWithRandomBlob(200);
+
+		writePackWithRandomBlob(300);
+		ObjectId blobTwo = writePackWithRandomBlob(100);
+		System.out.println(
+				"pack count: " + db.getObjectDatabase().getPacks().length);
+
+		DfsPackFile[] packs = db.getObjectDatabase().getPacks();
+		// Packs are in reverse insertion order
+		DfsPackFileMidx midxBase = writeMultipackIndex(
+				Arrays.copyOfRange(packs, 3, 5), null);
+		DfsPackFileMidx midxTip = writeMultipackIndex(
+				Arrays.copyOfRange(packs, 0, 3), midxBase);
+
+		try (RevWalk rw = new RevWalk(db);
+				DfsReader ctx = db.getObjectDatabase().newReader()) {
+			RevCommit aCommit = rw.parseCommit(commit);
+			RevTree aTree = rw.parseTree(aCommit.getTree());
+
+			assertEquals(168, midxTip.getObjectSize(ctx, aCommit));
+			assertEquals(33, midxTip.getObjectSize(ctx, aTree));
+			assertEquals(200, midxTip.getObjectSize(ctx, blob));
+			assertEquals(100, midxTip.getObjectSize(ctx, blobTwo));
+
+			assertEquals(-1, midxTip.getObjectSize(ctx, NOT_IN_PACK));
+		}
+	}
+
+	@Test
+	public void midx_getObjectSize_byOffset() throws Exception {
+		ObjectId commit = writePackWithCommit();
+		ObjectId blob = writePackWithRandomBlob(200);
+		DfsPackFile midx = writeMultipackIndex();
+
+		try (RevWalk rw = new RevWalk(db);
+				DfsReader ctx = db.getObjectDatabase().newReader()) {
+			RevCommit aCommit = rw.parseCommit(commit);
+			RevTree aTree = rw.parseTree(aCommit.getTree());
+			RevBlob aBlob = rw.lookupBlob(blob);
+
+			assertEquals(168,
+					midx.getObjectSize(ctx, midx.findOffset(ctx, aCommit)));
+			assertEquals(33,
+					midx.getObjectSize(ctx, midx.findOffset(ctx, aTree)));
+			assertEquals(200,
+					midx.getObjectSize(ctx, midx.findOffset(ctx, aBlob)));
+
+			assertEquals(-1, midx.getObjectSize(ctx, -1));
+		}
+	}
+
+	@Test
+	public void midx_getObjectSize_byOffset_withBase() throws Exception {
+		ObjectId commit = writePackWithCommit();
+		ObjectId blob = writePackWithRandomBlob(200);
+
+		writePackWithRandomBlob(300);
+		ObjectId blobTwo = writePackWithRandomBlob(100);
+
+		DfsPackFile[] packs = db.getObjectDatabase().getPacks();
+		// Packs are in reverse insertion order
+		DfsPackFileMidx midxBase = writeMultipackIndex(
+				Arrays.copyOfRange(packs, 3, 5), null);
+		DfsPackFileMidx midxTip = writeMultipackIndex(
+				Arrays.copyOfRange(packs, 0, 3), midxBase);
+
+		try (RevWalk rw = new RevWalk(db);
+				DfsReader ctx = db.getObjectDatabase().newReader()) {
+			RevCommit aCommit = rw.parseCommit(commit);
+			RevTree aTree = rw.parseTree(aCommit.getTree());
+
+			assertEquals(168, midxTip.getObjectSize(ctx,
+					midxTip.findOffset(ctx, aCommit)));
+			assertEquals(33,
+					midxTip.getObjectSize(ctx, midxTip.findOffset(ctx, aTree)));
+			assertEquals(200,
+					midxTip.getObjectSize(ctx, midxTip.findOffset(ctx, blob)));
+			assertEquals(100, midxTip.getObjectSize(ctx,
+					midxTip.findOffset(ctx, blobTwo)));
+
+			assertEquals(-1, midxTip.getObjectSize(ctx, -1));
+			assertEquals(-1, midxTip.getObjectSize(ctx,
+					midxTip.findOffset(ctx, NOT_IN_PACK)));
+		}
+	}
+
+	@Test
+	public void midx_objectSizeIndex_disabled() throws Exception {
+		writePackWithCommit();
+		writePackWithRandomBlob(200);
+		DfsPackFile midx = writeMultipackIndex();
+
+		try (DfsReader ctx = db.getObjectDatabase().newReader()) {
+			assertFalse(midx.hasObjectSizeIndex(ctx));
+		}
+	}
+
+	@Test
+	public void midx_fillRepresentation() throws Exception {
+		writePackWithRandomBlob(200);
+		RevCommit commit = writePackWithCommit();
+		DfsPackFile midx = writeMultipackIndex();
+
+		DfsObjectRepresentation rep = fillRepresentation(midx, commit,
+				OBJ_COMMIT);
+		assertEquals(midx, rep.pack);
+		assertEquals(347, rep.offset);
+		assertEquals(120, rep.length);
+	}
+
+	DfsObjectRepresentation fillRepresentation(DfsPackFile midx,
+			ObjectId commit, int typeHint) throws Exception {
+		try (DfsReader ctx = db.getObjectDatabase().newReader()) {
+			ObjectToPack otp = new DfsObjectToPack(commit, typeHint);
+			DfsObjectToPack inPack = midx
+					.findAllFromPack(ctx, List.of(otp), true).get(0);
+			DfsObjectRepresentation rep = new DfsObjectRepresentation(midx);
+			midx.fillRepresentation(rep, inPack.getOffset(), ctx);
+			return rep;
+		}
+	}
+
+	@Test
+	public void midx_fillRepresentation_withBase() throws Exception {
+		RevCommit commitInBase = writePackWithCommit();
+		ObjectId blob = writePackWithRandomBlob(300);
+		writePackWithRandomBlob(500);
+		writePackWithRandomBlob(100);
+		RevCommit commitInTip = writePackWithCommit();
+
+		DfsPackFile[] packs = db.getObjectDatabase().getPacks();
+		assertEquals(6, packs.length);
+		DfsPackFileMidx midxBase = writeMultipackIndex(
+				Arrays.copyOfRange(packs, 3, 6), null);
+		DfsPackFileMidx midxTip = writeMultipackIndex(
+				Arrays.copyOfRange(packs, 0, 3), midxBase);
+
+		try (DfsReader ctx = db.getObjectDatabase().newReader()) {
+			// Commit in tip midx
+			DfsObjectRepresentation rep = fillRepresentation(midxTip,
+					commitInTip, OBJ_COMMIT);
+			assertEquals(midxTip.getPackDescription(),
+					rep.pack.getPackDescription());
+			assertEquals(midxTip.findOffset(ctx, commitInTip), rep.offset);
+			assertEquals(150, rep.length);
+
+			// Commit in base midx
+			rep = fillRepresentation(midxTip, commitInBase, OBJ_COMMIT);
+			assertEquals(midxTip.getPackDescription(),
+					rep.pack.getPackDescription());
+			assertEquals(midxTip.findOffset(ctx, commitInBase), rep.offset);
+			assertEquals(120, rep.length);
+
+			// Blob in base
+			rep = fillRepresentation(midxTip, blob, OBJ_COMMIT);
+			assertEquals(midxTip.getPackDescription(),
+					rep.pack.getPackDescription());
+			assertEquals(midxTip.findOffset(ctx, blob), rep.offset);
+			assertEquals(311, rep.length);
+		}
+	}
+
+	@Test
+	public void midx_getBitmapIndex() throws Exception {
+		RevCommit c1 = writePackWithCommit();
+		RevCommit c2 = writePackWithCommit();
+		gcWithBitmaps();
+
+		ObjectId blob = writePackWithRandomBlob(300);
+		DfsPackFileMidx dfsPackFileMidx = writeMultipackIndex();
+		try (DfsReader ctx = db.getObjectDatabase().newReader()) {
+			PackBitmapIndex bitmapIndex = dfsPackFileMidx.getBitmapIndex(ctx);
+			assertNotNull(bitmapIndex);
+			// Both commits have same tree and blob
+			assertEquals(4, bitmapIndex.getObjectCount());
+			assertEquals(1, bitmapIndex.findPosition(c1));
+			assertEquals(0, bitmapIndex.findPosition(c2));
+			assertEquals(-1, bitmapIndex.findPosition(blob));
+		}
+	}
+
+	@Test
+	public void midx_getAllCoveredPacks() throws Exception {
+		writePackWithCommit();
+		writePackWithRandomBlob(300);
+		writePackWithRandomBlob(500);
+
+		DfsPackFile[] packs = db.getObjectDatabase().getPacks();
+		assertEquals(4, packs.length);
+		DfsPackFileMidx midx = writeMultipackIndex(packs, null);
+
+		assertEquals(4, midx.getAllCoveredPacks().size());
+		List<DfsPackDescription> expected = Arrays.stream(packs)
+				.map(p -> p.getPackDescription()).toList();
+		List<DfsPackDescription> actual = midx.getAllCoveredPacks().stream()
+				.map(DfsPackFile::getPackDescription).toList();
+		assertEquals(expected, actual);
+	}
+
+	@Test
+	public void midx_getAllCoveredPacks_withBase() throws Exception {
+		writePackWithCommit();
+		writePackWithRandomBlob(300);
+		writePackWithRandomBlob(500);
+		writePackWithRandomBlob(100);
+		writePackWithCommit();
+
+		DfsPackFile[] packs = db.getObjectDatabase().getPacks();
+		assertEquals(6, packs.length);
+		DfsPackFileMidx midxBase = writeMultipackIndex(
+				Arrays.copyOfRange(packs, 4, 6), null);
+		DfsPackFileMidx midxMiddle = writeMultipackIndex(
+				Arrays.copyOfRange(packs, 2, 4), midxBase);
+		DfsPackFileMidx midxTip = writeMultipackIndex(
+				Arrays.copyOfRange(packs, 0, 2), midxMiddle);
+
+		assertEquals(6, midxTip.getAllCoveredPacks().size());
+		List<DfsPackDescription> expected = Arrays.stream(packs)
+				.map(DfsPackFile::getPackDescription).toList();
+		List<DfsPackDescription> actual = midxTip.getAllCoveredPacks().stream()
+				.map(DfsPackFile::getPackDescription).toList();
+		assertEquals(expected, actual);
+	}
+
+	@Test
+	public void midx_getCoveredPacks_withBase_onlyTopMidx() throws Exception {
+		writePackWithCommit();
+		writePackWithRandomBlob(300);
+		writePackWithRandomBlob(500);
+		writePackWithRandomBlob(100);
+		writePackWithCommit();
+
+		DfsPackFile[] packs = db.getObjectDatabase().getPacks();
+		assertEquals(6, packs.length);
+		DfsPackFileMidx midxBase = writeMultipackIndex(
+				Arrays.copyOfRange(packs, 3, 6), null);
+		DfsPackFileMidx midxTip = writeMultipackIndex(
+				Arrays.copyOfRange(packs, 0, 3), midxBase);
+
+		assertEquals(3, midxTip.getCoveredPacks().size());
+		List<DfsPackDescription> expected = Arrays
+				.stream(Arrays.copyOfRange(packs, 0, 3))
+				.map(DfsPackFile::getPackDescription).toList();
+		List<DfsPackDescription> actual = midxTip.getCoveredPacks().stream()
+				.map(DfsPackFile::getPackDescription).toList();
+		assertEquals(expected, actual);
+	}
+
+	@Test
+	public void midx_corrupt() throws Exception {
+		RevCommit commit = writePackWithCommit();
+		writePackWithRandomBlob(200);
+		DfsPackFile midx = writeMultipackIndex();
+		try (DfsReader ctx = db.getObjectDatabase().newReader()) {
+			assertFalse(midx.isCorrupt(midx.findOffset(ctx, commit)));
+		}
+	}
+
+	@Test
+	public void packwriter_via_midx() throws Exception {
+		RevCommit commit = writePackWithCommit();
+		ObjectId blob = writePackWithBlob("booooohooooo".getBytes(UTF_8));
+		ObjectId notPacked = writePackWithBlob("baaaaahaa".getBytes(UTF_8));
+		writeMultipackIndex();
+
+		byte[] writtenPack;
+		try (DfsReader ctx = db.getObjectDatabase().newReader();
+				RevWalk rw = new RevWalk(ctx);
+				PackWriter pw = new PackWriter(db, ctx)) {
+			pw.addObject(rw.lookupBlob(blob));
+			pw.addObject(rw.lookupCommit(commit));
+
+			try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+				CounterProgressMonitor cpm = new CounterProgressMonitor();
+				pw.writePack(NullProgressMonitor.INSTANCE, cpm, out);
+				out.flush();
+				assertEquals(178, out.size());
+				assertEquals(2, cpm.objectsPacked);
+				writtenPack = out.toByteArray();
+			}
+		}
+
+		try (InMemoryRepository dest = new InMemoryRepository(
+				new DfsRepositoryDescription("test"));
+				ObjectInserter ins = dest.getObjectDatabase().newInserter()) {
+			ins.newPackParser(new ByteArrayInputStream(writtenPack))
+					.parse(NullProgressMonitor.INSTANCE);
+			assertTrue(dest.getObjectDatabase().has(commit));
+			assertTrue(dest.getObjectDatabase().has(blob));
+			assertFalse(dest.getObjectDatabase().has(notPacked));
+		}
+	}
+
+	@Test
+	public void voffsetcalculator_encode() {
+		DfsPackFile one = createDfsPackFile(800);
+		DfsPackFile two = createDfsPackFile(1200);
+		DfsPackFile three = createDfsPackFile(900);
+
+		VOffsetCalculator calc = VOffsetCalculator
+				.fromPacks(new DfsPackFile[] { one, two, three }, null);
+
+		PackOffset po = PackOffset.create(0, 12);
+		assertEquals(12, calc.encode(po));
+		po = PackOffset.create(1, 12);
+		assertEquals(800 + 12, calc.encode(po));
+		po = PackOffset.create(2, 12);
+		assertEquals(800 + 1200 + 12, calc.encode(po));
+	}
+
+	@Test
+	public void voffsetcalculator_decode() {
+		DfsPackFile one = createDfsPackFile(800);
+		DfsPackFile two = createDfsPackFile(1200);
+		DfsPackFile three = createDfsPackFile(900);
+
+		VOffsetCalculator calc = VOffsetCalculator
+				.fromPacks(new DfsPackFile[] { one, two, three }, null);
+
+		// In first pack
+		DfsPackFileMidx.DfsPackOffset decoded = calc.decode(130);
+		assertEquals(one.getPackDescription(),
+				decoded.getPack().getPackDescription());
+		assertEquals(130, decoded.getPackOffset());
+		assertEquals(0, decoded.getPackStart());
+
+		// In second pack
+		decoded = calc.decode(812);
+		assertEquals(two.getPackDescription(),
+				decoded.getPack().getPackDescription());
+		assertEquals(12, decoded.getPackOffset());
+		assertEquals(800, decoded.getPackStart());
+
+		// In third pack
+		decoded = calc.decode(2100);
+		assertEquals(two.getPackDescription(),
+				decoded.getPack().getPackDescription());
+		assertEquals(100, decoded.getPackOffset());
+		assertEquals(2000, decoded.getPackStart());
+	}
+
+	@Test
+	public void voffsetcalculator_notFound() {
+		DfsPackFile one = createDfsPackFile(800);
+		DfsPackFile two = createDfsPackFile(1200);
+		DfsPackFile three = createDfsPackFile(900);
+
+		VOffsetCalculator calc = VOffsetCalculator
+				.fromPacks(new DfsPackFile[] { one, two, three }, null);
+
+		assertEquals(-1, calc.encode(null));
+		assertNull(calc.decode(-1));
+	}
+
+	@Test
+	public void voffsetcalculator_maxOffset() {
+		DfsPackFile one = createDfsPackFile(800);
+		DfsPackFile two = createDfsPackFile(1200);
+		DfsPackFile three = createDfsPackFile(900);
+
+		long totalSize = one.getPackDescription().getFileSize(PACK)
+				+ two.getPackDescription().getFileSize(PACK)
+				+ three.getPackDescription().getFileSize(PACK);
+
+		VOffsetCalculator calc = VOffsetCalculator
+				.fromPacks(new DfsPackFile[] { one, two, three }, null);
+
+		assertEquals(totalSize, calc.getMaxOffset());
+	}
+
+	@Test
+	public void voffsetcalculator_withBase_encode() {
+		DfsPackFile one = createDfsPackFile(800);
+		DfsPackFile two = createDfsPackFile(1200);
+		DfsPackFile three = createDfsPackFile(900);
+
+		VOffsetCalculator baseCalc = VOffsetCalculator
+				.fromPacks(new DfsPackFile[] { one, two, three }, null);
+
+		DfsPackFile four = createDfsPackFile(900);
+		DfsPackFile five = createDfsPackFile(1300);
+		DfsPackFile six = createDfsPackFile(1000);
+
+		VOffsetCalculator calc = VOffsetCalculator
+				.fromPacks(new DfsPackFile[] { four, five, six }, baseCalc);
+
+		// These packIds are now from the second top midx
+		int firstMidxByteSize = 2900;
+		PackOffset po = PackOffset.create(0, 12);
+		assertEquals(12 + firstMidxByteSize, calc.encode(po));
+		po = PackOffset.create(1, 12);
+		assertEquals(900 + 12 + firstMidxByteSize, calc.encode(po));
+		po = PackOffset.create(2, 12);
+		assertEquals(900 + 1300 + 12 + firstMidxByteSize, calc.encode(po));
+	}
+
+	@Test
+	public void voffsetcalculator_withBase_decode() {
+		DfsPackFile one = createDfsPackFile(800);
+		DfsPackFile two = createDfsPackFile(1200);
+		DfsPackFile three = createDfsPackFile(900);
+		VOffsetCalculator baseCalc = VOffsetCalculator
+				.fromPacks(new DfsPackFile[] { one, two, three }, null);
+
+		DfsPackFile four = createDfsPackFile(900);
+		DfsPackFile five = createDfsPackFile(1300);
+		DfsPackFile six = createDfsPackFile(1000);
+
+		VOffsetCalculator calc = VOffsetCalculator
+				.fromPacks(new DfsPackFile[] { four, five, six }, baseCalc);
+
+		// In pack 1
+		DfsPackFileMidx.DfsPackOffset decoded = calc.decode(130);
+		assertEquals(one.getPackDescription(),
+				decoded.getPack().getPackDescription());
+		assertEquals(130, decoded.getPackOffset());
+		assertEquals(0, decoded.getPackStart());
+
+		// In pack 3
+		decoded = calc.decode(2300);
+		assertEquals(three.getPackDescription(),
+				decoded.getPack().getPackDescription());
+		assertEquals(300, decoded.getPackOffset());
+		assertEquals(2000, decoded.getPackStart());
+
+		// In pack 4
+		decoded = calc.decode(3000);
+		assertEquals(four.getPackDescription(),
+				decoded.getPack().getPackDescription());
+		assertEquals(100, decoded.getPackOffset());
+		assertEquals(2900, decoded.getPackStart());
+
+		// In pack 6
+		int packSixStart = 800 + 1200 + 900 + 900 + 1300;
+		decoded = calc.decode(packSixStart + 130);
+		assertEquals(six.getPackDescription(),
+				decoded.getPack().getPackDescription());
+		assertEquals(130, decoded.getPackOffset());
+		assertEquals(packSixStart, decoded.getPackStart());
+	}
+
+	@Test
+	public void voffsetcalculator_withBase_maxOffset() {
+		DfsPackFile one = createDfsPackFile(800);
+		DfsPackFile two = createDfsPackFile(1200);
+		DfsPackFile three = createDfsPackFile(900);
+
+		VOffsetCalculator baseCalc = VOffsetCalculator
+				.fromPacks(new DfsPackFile[] { one, two, three }, null);
+
+		DfsPackFile four = createDfsPackFile(900);
+		DfsPackFile five = createDfsPackFile(1300);
+		DfsPackFile six = createDfsPackFile(1000);
+
+		VOffsetCalculator calc = VOffsetCalculator
+				.fromPacks(new DfsPackFile[] { four, five, six }, baseCalc);
+
+		int expectedMaxOffset = 800 + 1200 + 900 + 900 + 1300 + 1000;
+		assertEquals(expectedMaxOffset, calc.getMaxOffset());
+	}
+
+	private static DfsPackFile createDfsPackFile(int size) {
+		DfsPackDescription desc = new DfsPackDescription(
+				new DfsRepositoryDescription("the_repo"), "pack_blabla", GC);
+		desc.addFileExt(PACK);
+		desc.setFileSize(PACK, size);
+		desc.setObjectCount(1);
+		return new DfsPackFile(null, desc);
+	}
+
+	private DfsPackFileMidx writeMultipackIndex() throws IOException {
+		return writeMultipackIndex(db.getObjectDatabase().getPacks(), null);
+	}
+
+	private void gcWithBitmaps() throws IOException {
+		DfsGarbageCollector garbageCollector = new DfsGarbageCollector(db);
+		garbageCollector.pack(NullProgressMonitor.INSTANCE);
+	}
+
+	private DfsPackFileMidx writeMultipackIndex(DfsPackFile[] packs,
+			DfsPackFileMidx base) throws IOException {
+		LinkedHashMap<String, PackIndex> forMidx = new LinkedHashMap<>(
+				packs.length);
+		Map<String, DfsPackDescription> descByName = new HashMap<>(
+				packs.length);
+		try (DfsReader ctx = db.getObjectDatabase().newReader()) {
+			for (DfsPackFile pack : packs) {
+				forMidx.put(pack.getPackDescription().getPackName(),
+						pack.getPackIndex(ctx));
+				descByName.put(pack.getPackDescription().getPackName(),
+						pack.getPackDescription());
+			}
+		}
+		MultiPackIndexWriter w = new MultiPackIndexWriter();
+		DfsPackDescription desc = db.getObjectDatabase().newPack(GC);
+		try (DfsOutputStream out = db.getObjectDatabase().writeFile(desc,
+				PackExt.MULTI_PACK_INDEX)) {
+			MultiPackIndexWriter.Result midxStats = w
+					.write(NullProgressMonitor.INSTANCE, out, forMidx);
+			desc.setCoveredPacks(midxStats.packNames().stream()
+					.map(descByName::get).toList());
+			desc.addFileExt(PackExt.MULTI_PACK_INDEX);
+		}
+		db.getObjectDatabase().commitPack(List.of(desc), null);
+		return DfsPackFileMidx.create(DfsBlockCache.getInstance(), desc,
+				Arrays.asList(packs), base);
+	}
+
+	private RevCommit writePackWithCommit() throws Exception {
+		try (TestRepository<InMemoryRepository> repository = new TestRepository<>(
+				db)) {
+			return repository.branch("/refs/heads/main").commit()
+					.add("blob1", "blob1").create();
+		}
+	}
+
+	private ObjectId writePackWithRandomBlob(int size) throws IOException {
+		byte[] data = new TestRng(JGitTestUtil.getName()).nextBytes(size);
+		return writePackWithBlob(data);
+	}
+
+	private ObjectId writePackWithBlob(byte[] data) throws IOException {
+		DfsInserter ins = (DfsInserter) db.newObjectInserter();
+		ins.setCompressionLevel(Deflater.NO_COMPRESSION);
+		ObjectId blobId = ins.insert(OBJ_BLOB, data);
+		ins.flush();
+		return blobId;
+	}
+
+	private DfsPackFile findPack(ObjectId oid) throws IOException {
+		DfsPackFile[] packs = db.getObjectDatabase().getPacks();
+		try (DfsReader ctx = db.getObjectDatabase().newReader()) {
+			for (DfsPackFile pack : packs) {
+				if (pack.hasObject(ctx, oid)) {
+					return pack;
+				}
+			}
+		}
+		throw new IllegalArgumentException("Object not in any pack");
+	}
+
+	private static final class CounterProgressMonitor
+			implements ProgressMonitor {
+
+		int objectsPacked = 0;
+
+		@Override
+		public void start(int totalTasks) {
+
+		}
+
+		@Override
+		public void beginTask(String title, int totalWork) {
+			System.out.println("Starting " + title);
+		}
+
+		@Override
+		public void update(int completed) {
+			objectsPacked += 1;
+		}
+
+		@Override
+		public void endTask() {
+
+		}
+
+		@Override
+		public boolean isCancelled() {
+			return false;
+		}
+
+		@Override
+		public void showDuration(boolean enabled) {
+
+		}
+	}
+
+	private byte[] safeGetBytes(@Nullable ObjectLoader ol) {
+		assertNotNull(ol);
+		byte[] data = ol.getBytes();
+		assertNotNull(data);
+		return data;
+	}
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/MidxPackFilterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/MidxPackFilterTest.java
new file mode 100644
index 0000000..584bab1
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/MidxPackFilterTest.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2025, 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.dfs;
+
+import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.COMPACT;
+import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.GC;
+import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.INSERT;
+import static org.eclipse.jgit.internal.storage.pack.PackExt.MULTI_PACK_INDEX;
+import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+
+import org.eclipse.jgit.internal.storage.pack.PackExt;
+import org.junit.Test;
+
+public class MidxPackFilterTest {
+	private static final DfsRepositoryDescription repoDesc = new DfsRepositoryDescription(
+			"test");
+
+	private static int timeCounter = 0;
+
+	@Test
+	public void useMidx_oneMidxCoversAll_onlyMidx() {
+		DfsPackDescription gc = pack("aaaa", GC, PACK);
+		DfsPackDescription compact = pack("cccc", COMPACT, PACK);
+		DfsPackDescription compactTwo = pack("bbbb", COMPACT, PACK);
+		DfsPackDescription midx = pack("midx", GC, MULTI_PACK_INDEX);
+		midx.setCoveredPacks(List.of(gc, compact, compactTwo));
+
+		List<DfsPackDescription> reorgPacks = MidxPackFilter
+				.useMidx(List.of(gc, compact, compactTwo, midx));
+		assertEquals(1, reorgPacks.size());
+		assertEquals(midx, reorgPacks.get(0));
+	}
+
+	@Test
+	public void useMidx_midxAndOneUncoveredPack_midxAndPack() {
+		DfsPackDescription gc = pack("aaaa", GC, PACK);
+		DfsPackDescription compact = pack("cccc", COMPACT, PACK);
+		DfsPackDescription compactTwo = pack("bbbb", COMPACT, PACK);
+		DfsPackDescription midx = pack("midx", GC, MULTI_PACK_INDEX);
+		midx.setCoveredPacks(List.of(gc, compact, compactTwo));
+
+		DfsPackDescription extra = pack("extra", COMPACT, PACK);
+
+		List<DfsPackDescription> reorgPacks = MidxPackFilter
+				.useMidx(List.of(gc, compact, compactTwo, midx, extra));
+		assertEquals(2, reorgPacks.size());
+		assertTrue(reorgPacks.contains(midx));
+		assertTrue(reorgPacks.contains(extra));
+	}
+
+	@Test
+	public void useMidx_noMidx_allPacks() {
+		DfsPackDescription gc = pack("aaaa", GC, PACK);
+		DfsPackDescription compact = pack("cccc", COMPACT, PACK);
+		DfsPackDescription compactTwo = pack("bbbb", COMPACT, PACK);
+
+		List<DfsPackDescription> reorgPacks = MidxPackFilter
+				.useMidx(List.of(gc, compact, compactTwo));
+		assertEquals(3, reorgPacks.size());
+		assertTrue(reorgPacks.contains(gc));
+		assertTrue(reorgPacks.contains(compact));
+		assertTrue(reorgPacks.contains(compactTwo));
+	}
+
+	@Test
+	public void useMidx_midxMissesOnePack_onlyPacks() {
+		DfsPackDescription gc = pack("aaaa", GC, PACK);
+		DfsPackDescription compact = pack("cccc", COMPACT, PACK);
+		DfsPackDescription compactTwo = pack("bbbb", COMPACT, PACK);
+		DfsPackDescription midx = pack("midx", GC, MULTI_PACK_INDEX);
+		midx.setCoveredPacks(List.of(gc, compact, compactTwo));
+
+		List<DfsPackDescription> reorgPacks = MidxPackFilter
+				.useMidx(List.of(gc, compactTwo, midx)); // compact missing
+		assertEquals(2, reorgPacks.size());
+		assertTrue(reorgPacks.contains(gc));
+		assertTrue(reorgPacks.contains(compactTwo));
+	}
+
+	@Test
+	public void useMidx_nestedMidx_onlyTopMidx() {
+		DfsPackDescription gc = pack("aaaa", GC, PACK);
+		DfsPackDescription compact = pack("cccc", COMPACT, PACK);
+		DfsPackDescription firstMidx = pack("midx1", GC, MULTI_PACK_INDEX);
+		firstMidx.setCoveredPacks(List.of(gc, compact));
+
+		DfsPackDescription compact2 = pack("dddd", COMPACT, PACK);
+		DfsPackDescription compact3 = pack("eeee", COMPACT, PACK);
+		DfsPackDescription topMidx = pack("midx2", GC, MULTI_PACK_INDEX);
+		topMidx.setCoveredPacks(List.of(compact2, compact3));
+		topMidx.setMultiPackIndexBase(firstMidx);
+
+		List<DfsPackDescription> reorgPacks = MidxPackFilter.useMidx(
+				List.of(gc, compact, firstMidx, compact2, compact3, topMidx));
+		assertEquals(1, reorgPacks.size());
+		assertTrue(reorgPacks.contains(topMidx));
+	}
+
+	@Test
+	public void useMidx_nestedMidxAndOnePack_topMidxAndPack() {
+		DfsPackDescription gc = pack("aaaa", GC, PACK);
+		DfsPackDescription compact = pack("cccc", COMPACT, PACK);
+		DfsPackDescription firstMidx = pack("midx1", GC, MULTI_PACK_INDEX);
+		firstMidx.setCoveredPacks(List.of(gc, compact));
+
+		DfsPackDescription compact2 = pack("dddd", COMPACT, PACK);
+		DfsPackDescription compact3 = pack("eeee", COMPACT, PACK);
+		DfsPackDescription topMidx = pack("midx2", GC, MULTI_PACK_INDEX);
+		topMidx.setCoveredPacks(List.of(compact2, compact3));
+		topMidx.setMultiPackIndexBase(firstMidx);
+
+		DfsPackDescription uncovered = pack("uncovered", INSERT, PACK);
+
+		List<DfsPackDescription> reorgPacks = MidxPackFilter.useMidx(List.of(gc,
+				compact, firstMidx, compact2, compact3, topMidx, uncovered));
+		assertEquals(2, reorgPacks.size());
+		assertTrue(reorgPacks.contains(topMidx));
+		assertTrue(reorgPacks.contains(uncovered));
+	}
+
+	@Test
+	public void skipMidx_oneMidxCoversAll_allPacks() {
+		DfsPackDescription gc = pack("aaaa", GC, PACK);
+		DfsPackDescription compact = pack("cccc", COMPACT, PACK);
+		DfsPackDescription compactTwo = pack("bbbb", COMPACT, PACK);
+		DfsPackDescription midx = pack("midx", GC, MULTI_PACK_INDEX);
+		midx.setCoveredPacks(List.of(gc, compact, compactTwo));
+
+		List<DfsPackDescription> reorgPacks = MidxPackFilter
+				.skipMidxs(List.of(gc, compact, compactTwo, midx));
+		assertEquals(3, reorgPacks.size());
+		assertTrue(reorgPacks.contains(gc));
+		assertTrue(reorgPacks.contains(compact));
+		assertTrue(reorgPacks.contains(compactTwo));
+	}
+
+	@Test
+	public void skipMidx_midxAndOneUncoveredPack_allPacks() {
+		DfsPackDescription gc = pack("aaaa", GC, PACK);
+		DfsPackDescription compact = pack("cccc", COMPACT, PACK);
+		DfsPackDescription compactTwo = pack("bbbb", COMPACT, PACK);
+		DfsPackDescription midx = pack("midx", GC, MULTI_PACK_INDEX);
+		midx.setCoveredPacks(List.of(gc, compact, compactTwo));
+
+		DfsPackDescription extra = pack("extra", COMPACT, PACK);
+
+		List<DfsPackDescription> reorgPacks = MidxPackFilter
+				.skipMidxs(List.of(gc, compact, compactTwo, midx, extra));
+		assertEquals(4, reorgPacks.size());
+		assertTrue(reorgPacks.contains(gc));
+		assertTrue(reorgPacks.contains(compact));
+		assertTrue(reorgPacks.contains(compactTwo));
+		assertTrue(reorgPacks.contains(extra));
+	}
+
+	@Test
+	public void skipMidx_noMidx_allPacks() {
+		DfsPackDescription gc = pack("aaaa", GC, PACK);
+		DfsPackDescription compact = pack("cccc", COMPACT, PACK);
+		DfsPackDescription compactTwo = pack("bbbb", COMPACT, PACK);
+
+		List<DfsPackDescription> reorgPacks = MidxPackFilter
+				.skipMidxs(List.of(gc, compact, compactTwo));
+		assertEquals(3, reorgPacks.size());
+		assertTrue(reorgPacks.contains(gc));
+		assertTrue(reorgPacks.contains(compact));
+		assertTrue(reorgPacks.contains(compactTwo));
+	}
+
+	@Test
+	public void skipMidx_nestedMidx_allPacks() {
+		DfsPackDescription gc = pack("aaaa", GC, PACK);
+		DfsPackDescription compact = pack("cccc", COMPACT, PACK);
+		DfsPackDescription firstMidx = pack("midx1", GC, MULTI_PACK_INDEX);
+		firstMidx.setCoveredPacks(List.of(gc, compact));
+
+		DfsPackDescription compact2 = pack("dddd", COMPACT, PACK);
+		DfsPackDescription compact3 = pack("eeee", COMPACT, PACK);
+		DfsPackDescription topMidx = pack("midx2", GC, MULTI_PACK_INDEX);
+		topMidx.setCoveredPacks(List.of(compact2, compact3));
+		topMidx.setMultiPackIndexBase(firstMidx);
+
+		List<DfsPackDescription> reorgPacks = MidxPackFilter.skipMidxs(
+				List.of(gc, compact, firstMidx, compact2, compact3, topMidx));
+		assertEquals(4, reorgPacks.size());
+		assertTrue(reorgPacks.contains(gc));
+		assertTrue(reorgPacks.contains(compact));
+		assertTrue(reorgPacks.contains(compact2));
+		assertTrue(reorgPacks.contains(compact3));
+	}
+
+	@Test
+	public void skipMidx_nestedMidxAndOnePack_allPacks() {
+		DfsPackDescription gc = pack("aaaa", GC, PACK);
+		DfsPackDescription compact = pack("cccc", COMPACT, PACK);
+		DfsPackDescription firstMidx = pack("midx1", GC, MULTI_PACK_INDEX);
+		firstMidx.setCoveredPacks(List.of(gc, compact));
+
+		DfsPackDescription compact2 = pack("dddd", COMPACT, PACK);
+		DfsPackDescription compact3 = pack("eeee", COMPACT, PACK);
+		DfsPackDescription topMidx = pack("midx2", GC, MULTI_PACK_INDEX);
+		topMidx.setCoveredPacks(List.of(compact2, compact3));
+		topMidx.setMultiPackIndexBase(firstMidx);
+
+		DfsPackDescription uncovered = pack("uncovered", INSERT, PACK);
+
+		List<DfsPackDescription> reorgPacks = MidxPackFilter
+				.skipMidxs(List.of(gc, compact, firstMidx, compact2, compact3,
+						topMidx, uncovered));
+		assertEquals(5, reorgPacks.size());
+		assertTrue(reorgPacks.contains(gc));
+		assertTrue(reorgPacks.contains(compact));
+		assertTrue(reorgPacks.contains(compact2));
+		assertTrue(reorgPacks.contains(compact3));
+		assertTrue(reorgPacks.contains(uncovered));
+	}
+
+	private static DfsPackDescription pack(String name,
+			DfsObjDatabase.PackSource source, PackExt ext) {
+		DfsPackDescription desc = new DfsPackDescription(repoDesc, name,
+				source);
+		desc.setLastModified(timeCounter++);
+		desc.addFileExt(ext);
+		return desc;
+	}
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/MidxPackListTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/MidxPackListTest.java
new file mode 100644
index 0000000..6ab1156
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/MidxPackListTest.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2025, 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.dfs;
+
+import static java.util.Arrays.asList;
+import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.zip.Deflater;
+
+import org.eclipse.jgit.junit.JGitTestUtil;
+import org.eclipse.jgit.junit.TestRng;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ObjectId;
+import org.junit.Before;
+import org.junit.Test;
+
+public class MidxPackListTest {
+
+	InMemoryRepository db;
+
+	@Before
+	public void setUp() {
+		db = new InMemoryRepository(new DfsRepositoryDescription("test"));
+		db.getObjectDatabase().setUseMultipackIndex(true);
+	}
+
+	@Test
+	public void getAllPlainPacks_onlyPlain() throws IOException {
+		setupThreePacks();
+		DfsPackFile[] packs = db.getObjectDatabase().getPacks();
+		MidxPackList packList = MidxPackList.create(packs);
+		assertEquals(3, packList.getAllPlainPacks().size());
+	}
+
+	@Test
+	public void getAllPlainPacks_onlyMidx() throws IOException {
+		setupThreePacksAndMidx();
+
+		DfsPackFile[] packs = db.getObjectDatabase().getPacks();
+		assertEquals(1, packs.length);
+		MidxPackList packList = MidxPackList.create(packs);
+		assertEquals(3, packList.getAllPlainPacks().size());
+	}
+
+	@Test
+	public void getAllPlainPacks_midxPlusOne() throws IOException {
+		setupThreePacksAndMidx();
+		writePackWithRandomBlob(60);
+
+		DfsPackFile[] packs = db.getObjectDatabase().getPacks();
+		assertEquals(2, packs.length);
+		MidxPackList packList = MidxPackList.create(packs);
+		assertEquals(4, packList.getAllPlainPacks().size());
+	}
+
+	@Test
+	public void getAllPlainPacks_nestedMidx() throws IOException {
+		setupSixPacksThreeMidx();
+
+		DfsPackFile[] packs = db.getObjectDatabase().getPacks();
+		assertEquals(1, packs.length);
+		MidxPackList packList = MidxPackList.create(packs);
+		assertEquals(6, packList.getAllPlainPacks().size());
+	}
+
+	@Test
+	public void getAllMidxPacks_onlyPlain() throws IOException {
+		setupThreePacks();
+
+		MidxPackList packList = MidxPackList
+				.create(db.getObjectDatabase().getPacks());
+		assertEquals(0, packList.getAllMidxPacks().size());
+	}
+
+	@Test
+	public void getAllMidxPacks_onlyMidx() throws IOException {
+		setupThreePacksAndMidx();
+
+		MidxPackList packList = MidxPackList
+				.create(db.getObjectDatabase().getPacks());
+		assertEquals(1, packList.getAllMidxPacks().size());
+	}
+
+	@Test
+	public void getAllMidxPacks_midxPlusOne() throws IOException {
+		setupThreePacksAndMidx();
+		writePackWithRandomBlob(60);
+
+		DfsPackFile[] packs = db.getObjectDatabase().getPacks();
+		assertEquals(2, packs.length);
+		MidxPackList packList = MidxPackList.create(packs);
+		assertEquals(1, packList.getAllMidxPacks().size());
+	}
+
+	@Test
+	public void getAllMidxPacks_nestedMidx() throws IOException {
+		setupSixPacksThreeMidx();
+
+		DfsPackFile[] packs = db.getObjectDatabase().getPacks();
+		assertEquals(1, packs.length);
+		MidxPackList packList = MidxPackList.create(packs);
+		assertEquals(3, packList.getAllMidxPacks().size());
+	}
+
+	@Test
+	public void findAllImpactedMidx_onlyPacks() throws IOException {
+		setupThreePacks();
+
+		DfsPackFile[] packs = db.getObjectDatabase().getPacks();
+		MidxPackList packList = MidxPackList
+				.create(db.getObjectDatabase().getPacks());
+		assertEquals(0, packList.findAllCoveringMidxs(asList(packs)).size());
+	}
+
+	@Test
+	public void findAllImpactedMidx_onlyMidx() throws IOException {
+		setupThreePacksAndMidx();
+
+		DfsPackFile[] packs = db.getObjectDatabase().getPacks();
+		MidxPackList packList = MidxPackList.create(packs);
+		List<DfsPackFile> covered = ((DfsPackFileMidx) packs[0])
+				.getAllCoveredPacks();
+		assertEquals(1, packList.findAllCoveringMidxs(covered.get(0)).size());
+		assertEquals(1, packList.findAllCoveringMidxs(covered.get(1)).size());
+		assertEquals(1, packList.findAllCoveringMidxs(covered.get(2)).size());
+
+		assertEquals("multiple packs covered", 1,
+				packList.findAllCoveringMidxs(covered.subList(0, 2)).size());
+	}
+
+	@Test
+	public void findAllImpactedMidx_midxPlusOne() throws IOException {
+		setupThreePacksAndMidx();
+		writePackWithRandomBlob(60);
+
+		DfsPackFile[] packs = db.getObjectDatabase().getPacks();
+		assertEquals(2, packs.length);
+
+		DfsPackFile uncoveredPack = packs[0];
+		List<DfsPackFile> coveredPacks = ((DfsPackFileMidx) packs[1])
+				.getAllCoveredPacks();
+		assertEquals(3, coveredPacks.size());
+
+		MidxPackList packList = MidxPackList.create(packs);
+		assertEquals("one non covered", 0,
+				packList.findAllCoveringMidxs(uncoveredPack).size());
+		assertEquals("one and covered", 1,
+				packList.findAllCoveringMidxs(coveredPacks.get(1)).size());
+		assertEquals(
+				"two, only one covered", 1, packList
+						.findAllCoveringMidxs(
+								List.of(uncoveredPack, coveredPacks.get(2)))
+						.size());
+	}
+
+	@Test
+	public void findAllImpactedMidxs_nestedMidx() throws IOException {
+		setupSixPacksThreeMidx();
+
+		DfsPackFile[] packs = db.getObjectDatabase().getPacks();
+		assertEquals(1, packs.length);
+		MidxPackList packList = MidxPackList.create(packs);
+		List<DfsPackFile> coveredPacks = ((DfsPackFileMidx) packs[0])
+				.getAllCoveredPacks();
+		assertEquals(6, coveredPacks.size());
+
+		assertEquals("one covered tip midx", 1,
+				packList.findAllCoveringMidxs(coveredPacks.get(0)).size());
+		assertEquals("one covered middle midx", 2,
+				packList.findAllCoveringMidxs(coveredPacks.get(2)).size());
+		assertEquals("one covered base midx", 3,
+				packList.findAllCoveringMidxs(coveredPacks.get(4)).size());
+		assertEquals(
+				"multiple covered in chain", 3, packList
+						.findAllCoveringMidxs(List.of(coveredPacks.get(1),
+								coveredPacks.get(2), coveredPacks.get(5)))
+						.size());
+	}
+
+
+
+	private void setupThreePacks() throws IOException {
+		writePackWithRandomBlob(100);
+		writePackWithRandomBlob(300);
+		writePackWithRandomBlob(50);
+	}
+
+	private void setupThreePacksAndMidx() throws IOException {
+		writePackWithRandomBlob(100);
+		writePackWithRandomBlob(300);
+		writePackWithRandomBlob(50);
+		writeMultipackIndex();
+	}
+
+	private void setupSixPacksThreeMidx() throws IOException {
+		writePackWithRandomBlob(100);
+		writePackWithRandomBlob(300);
+		writePackWithRandomBlob(50);
+		writePackWithRandomBlob(400);
+		writePackWithRandomBlob(130);
+		writePackWithRandomBlob(500);
+		DfsPackFile[] packs = db.getObjectDatabase().getPacks();
+		assertEquals(6, packs.length);
+		DfsPackFileMidx midxBase = writeMultipackIndex(
+				Arrays.copyOfRange(packs, 4, 6), null);
+		DfsPackFileMidx midxMid = writeMultipackIndex(
+				Arrays.copyOfRange(packs, 2, 4), midxBase);
+		writeMultipackIndex(Arrays.copyOfRange(packs, 0, 2), midxMid);
+		assertEquals("only top midx", 1,
+				db.getObjectDatabase().getPacks().length);
+	}
+
+	private void writeMultipackIndex() throws IOException {
+		writeMultipackIndex(db.getObjectDatabase().getPacks(), null);
+	}
+
+	private DfsPackFileMidx writeMultipackIndex(DfsPackFile[] packs,
+			DfsPackFileMidx base) throws IOException {
+		List<DfsPackFile> packfiles = asList(packs);
+		DfsPackDescription desc = DfsMidxWriter.writeMidx(
+				NullProgressMonitor.INSTANCE, db.getObjectDatabase(), packfiles,
+				base != null ? base.getPackDescription() : null);
+		db.getObjectDatabase().commitPack(List.of(desc), null);
+		return DfsPackFileMidx.create(DfsBlockCache.getInstance(), desc,
+				packfiles, base);
+	}
+
+	private ObjectId writePackWithBlob(byte[] data) throws IOException {
+		DfsInserter ins = (DfsInserter) db.newObjectInserter();
+		ins.setCompressionLevel(Deflater.NO_COMPRESSION);
+		ObjectId blobId = ins.insert(OBJ_BLOB, data);
+		ins.flush();
+		return blobId;
+	}
+
+	// Do not use the size twice into the same test (it gives the same blob!)
+	private ObjectId writePackWithRandomBlob(int size) throws IOException {
+		byte[] data = new TestRng(JGitTestUtil.getName()).nextBytes(size);
+		return writePackWithBlob(data);
+	}
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/BasePackWriterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/BasePackWriterTest.java
index cd73c6a..b8404aa 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/BasePackWriterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/BasePackWriterTest.java
@@ -16,6 +16,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
@@ -63,7 +64,9 @@
 import org.eclipse.jgit.transport.PackParser;
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TestName;
 import org.mockito.Mockito;
 
 public class BasePackWriterTest extends SampleDataRepositoryTestCase {
@@ -74,6 +77,8 @@ public class BasePackWriterTest extends SampleDataRepositoryTestCase {
 	private static final Set<ObjectIdSet> EMPTY_ID_SET = Collections
 			.<ObjectIdSet> emptySet();
 
+	private static final boolean UNSHALLOW_FETCH = true;
+
 	private PackConfig config;
 
 	private PackWriter writer;
@@ -96,6 +101,10 @@ public class BasePackWriterTest extends SampleDataRepositoryTestCase {
 
 	private RevBlob contentE;
 
+	private RevBlob contentF;
+
+	private RevBlob contentG;
+
 	private RevCommit c1;
 
 	private RevCommit c2;
@@ -106,6 +115,13 @@ public class BasePackWriterTest extends SampleDataRepositoryTestCase {
 
 	private RevCommit c5;
 
+	private RevCommit c6;
+
+	private RevCommit c7;
+
+	@Rule
+	public TestName testName = new TestName();
+
 	@Override
 	@Before
 	public void setUp() throws Exception {
@@ -205,12 +221,9 @@ public void testWriteEmptyPack2() throws IOException {
 	public void testNotIgnoreNonExistingObjects() throws IOException {
 		final ObjectId nonExisting = ObjectId
 				.fromString("0000000000000000000000000000000000000001");
-		try {
-			createVerifyOpenPack(NONE, haves(nonExisting), false, false);
-			fail("Should have thrown MissingObjectException");
-		} catch (MissingObjectException x) {
-			// expected
-		}
+		assertThrows(MissingObjectException.class,
+				() -> createVerifyOpenPack(NONE, haves(nonExisting), false,
+						false));
 	}
 
 	/**
@@ -599,13 +612,19 @@ public void testExclude() throws Exception {
 	}
 
 	private static void assertContent(PackIndex pi, List<ObjectId> expected) {
-		assertEquals("Pack index has wrong size.", expected.size(),
-				pi.getObjectCount());
-		for (int i = 0; i < pi.getObjectCount(); i++)
-			assertTrue(
-					"Pack index didn't contain the expected id "
-							+ pi.getObjectId(i),
-					expected.contains(pi.getObjectId(i)));
+		Set<ObjectId> packedObjectIds = new HashSet<>();
+		for (int i = 0; i < pi.getObjectCount(); i++) {
+			packedObjectIds.add(pi.getObjectId(i));
+		}
+		for (ObjectId packedObjectId : packedObjectIds) {
+			assertTrue("Unexpected id in pack index: " + packedObjectId,
+				expected.contains(packedObjectId));
+		}
+		for (ObjectId expectedObjectId : expected) {
+			assertTrue("Pack index didn't contain the expected id " + expectedObjectId,
+				packedObjectIds.contains(expectedObjectId));
+		}
+		assertEquals("Pack index has wrong size.", expected.size(), pi.getObjectCount());
 	}
 
 	@Test
@@ -702,6 +721,141 @@ public void testShallowFetchShallowAncestorDepth2() throws Exception {
 	}
 
 	@Test
+	public void testUnshallowFetchShallowAncestorToMaxDepth() throws Exception {
+		//     [unshallow-branch]
+		//         |
+		//         v
+		//c1 <--- c2 <--- c3 <--- c4 <---  c5 <--- c6 <-[main-branch]
+		//                ^
+		//                |
+		//         [stable-branch]
+		//
+		// The above graph shows the test-case of a linear history created
+		// by setupRepoForUnshallowFetch(), where we simulate the branches
+		// by wanting both c6 (as 'main-branch') and c3 (as 'stable-branch').
+		try (FileRepository repo = setupRepoForUnshallowFetch()) {
+			// The client has a shallow clone with depth=1 of c2 and then
+			// requests the unshallow with WANT=stable-branch(c3) and main-branch(c6)
+			// notifying the server that HAVE, therefore requesting a maximum depth
+			// of Integer.MAX_VALUE
+			PackIndex idx = writeUnshallowPack(repo, Integer.MAX_VALUE, wants(c6, c3), haves(c2));
+			assertContent(idx,
+				Arrays.asList(c1.getId(), c1.getTree().getId(),
+					// NOTE: c2, its tree and BLOB are not expected to be packed
+					// because the existing shallow repo should have them already
+					c3.getId(), c3.getTree().getId(),
+					c4.getId(), c4.getTree().getId(),
+					c5.getId(), c5.getTree().getId(),
+					c6.getId(), c6.getTree().getId(),
+					// NOTE: contentA.getId() is already local as it is referenced by c2's tree
+					// which is part of the initial shallow clone with depth=1
+					contentC.getId(),
+					contentD.getId(), contentE.getId(), contentF.getId()));
+		}
+	}
+
+	@Test
+	public void testUnshallowFetchShallowMergeCommitToMaxDepth() throws Exception {
+		//                  [unshallow-branch]
+		//                        |
+		//                        v
+		//c1 <--- c2 <--- c5 <--- c6 <--- c7 <-[main-branch]
+		//         \             /
+		//          -- c3 <--- c4  <-[feature-branch]
+		//
+		try (FileRepository repo = setupRepoWithMergeCommitForUnshallowFetch()) {
+			PackIndex idx = writeUnshallowPack(repo, Integer.MAX_VALUE, wants(c7, c4), haves(c6));
+			assertContent(idx,
+				Arrays.asList(c1.getId(), c1.getTree().getId(),
+					c2.getId(), c2.getTree().getId(),
+					c3.getId(), c3.getTree().getId(),
+					c4.getId(), c4.getTree().getId(),
+					c5.getId(), c5.getTree().getId(),
+					c7.getId(), c7.getTree().getId(),
+					// contentA, contentB, contentE and contentF are already part of c6 tree
+					contentC.getId(),
+					contentD.getId(), contentG.getId()));
+		}
+	}
+
+	@Test
+	public void testUnshallowFetchShallowLinearCommitToMaxDepth() throws Exception {
+		//           [unshallow-branch]
+		//                 |
+		//                 v
+		//c1 <--- c2 <--- c3 <--- c4 <--- c5 <-[main-branch]
+		//
+		try (FileRepository repo = setupRepoWithLinearHistoryForUnshallowFetch()) {
+			PackIndex idx = writeUnshallowPack(repo, Integer.MAX_VALUE, wants(c5), haves(c3));
+			assertContent(idx,
+				Arrays.asList(c1.getId(), c1.getTree().getId(),
+					c2.getId(), c2.getTree().getId(),
+					c4.getId(), c4.getTree().getId(),
+					c5.getId(), c5.getTree().getId(),
+					// contentA, contentB are already part of c3 tree
+					contentD.getId(), contentE.getId()));
+		}
+	}
+
+	private FileRepository setupRepoWithMergeCommitForUnshallowFetch() throws Exception {
+		String commitMessage = testName.getMethodName();
+		FileRepository repo = createBareRepository();
+		// TestRepository will close the repo, but we need to return an open
+		// one!
+		repo.incrementOpen();
+		try (TestRepository<Repository> r = new TestRepository<>(repo)) {
+			BranchBuilder bb = r.branch("refs/heads/master");
+			contentA = r.blob("A");
+			contentB = r.blob("B");
+			contentC = r.blob("C");
+			contentD = r.blob("D");
+			contentE = r.blob("E");
+			contentF = r.blob("F");
+			contentG = r.blob("G");
+			c1 = bb.commit().message(commitMessage + " c1").add("a", contentA).create();
+			c2 = bb.commit().message(commitMessage + " c2").add("b", contentB).create();
+			c5 = bb.commit().message(commitMessage + " c5").add("e", contentE).create();
+
+			BranchBuilder fb = r.branch("refs/heads/feature");
+			fb.update(c2);
+			c3 = fb.commit().message(commitMessage + " c3").add("c", contentC).create();
+			c4 = fb.commit().message(commitMessage + " c4").add("d", contentD).create();
+
+			c6 = bb.commit().message(commitMessage + " c6").add("f", contentF).parent(c4).create();
+			r.branch("refs/heads/unshallow").update(c6);
+
+			c7 = bb.commit().message(commitMessage + " c7").add("g", contentG).create();
+			r.getRevWalk().parseHeaders(c7); // fully initialize the tip RevCommit
+
+			return repo;
+		}
+	}
+
+	private FileRepository setupRepoWithLinearHistoryForUnshallowFetch() throws Exception {
+		String commitMessage = testName.getMethodName();
+		FileRepository repo = createBareRepository();
+		// TestRepository will close the repo, but we need to return an open
+		// one!
+		repo.incrementOpen();
+		try (TestRepository<Repository> r = new TestRepository<>(repo)) {
+			BranchBuilder bb = r.branch("refs/heads/master");
+			contentA = r.blob("A");
+			contentB = r.blob("B");
+			contentC = r.blob("C");
+			contentD = r.blob("D");
+			contentE = r.blob("E");
+			c1 = bb.commit().message(commitMessage + " c1").add("a", contentA).create();
+			c2 = bb.commit().message(commitMessage + " c2").add("b", contentB).create();
+			c3 = bb.commit().message(commitMessage + " c3").add("c", contentC).create();
+			c4 = bb.commit().message(commitMessage + " c4").add("d", contentD).create();
+			c5 = bb.commit().message(commitMessage + " c5").add("e", contentE).create();
+			r.branch("refs/heads/unshallow").update(c3);
+			r.getRevWalk().parseHeaders(c5); // fully initialize the tip RevCommit
+			return repo;
+		}
+	}
+
+	@Test
 	public void testTotalPackFilesScanWhenSearchForReuseTimeoutNotSetTrue()
 			throws Exception {
 		totalPackFilesScanWhenSearchForReuseTimeoutNotSet(true);
@@ -713,6 +867,7 @@ public void testTotalPackFilesScanWhenSearchForReuseTimeoutNotSetFalse()
 		totalPackFilesScanWhenSearchForReuseTimeoutNotSet(false);
 	}
 
+	@SuppressWarnings("boxing")
 	public void totalPackFilesScanWhenSearchForReuseTimeoutNotSet(boolean doReturn) throws Exception {
 		FileRepository fileRepository = setUpRepoWithMultiplePackfiles();
 		int numberOfPackFiles = (int) new GC(fileRepository).getStatistics().numberOfPackFiles;
@@ -748,6 +903,7 @@ public void testTotalPackFilesScanWhenSkippingSearchForReuseTimeoutCheckFalse()
 		totalPackFilesScanWhenSkippingSearchForReuseTimeoutCheck(false);
 	}
 
+	@SuppressWarnings("boxing")
 	public void totalPackFilesScanWhenSkippingSearchForReuseTimeoutCheck(
 			boolean doReturn) throws Exception {
 		FileRepository fileRepository = setUpRepoWithMultiplePackfiles();
@@ -784,6 +940,7 @@ public void partialPackFilesScanWhenDoingSearchForReuseTimeoutCheck()
 		testPartialPackFilesScanWhenDoingSearchForReuseTimeoutCheck(false, expectedSelectCalls);
 	}
 
+	@SuppressWarnings("boxing")
 	public void testPartialPackFilesScanWhenDoingSearchForReuseTimeoutCheck(
 			boolean doReturn, int expectedSelectCalls) throws Exception {
 		FileRepository fileRepository = setUpRepoWithMultiplePackfiles();
@@ -856,6 +1013,7 @@ private PackFile getPackFileToWrite(FileRepository fileRepository,
 	}
 
 	private FileRepository setupRepoForShallowFetch() throws Exception {
+		String commitMessage = testName.getMethodName();
 		FileRepository repo = createBareRepository();
 		// TestRepository will close the repo, but we need to return an open
 		// one!
@@ -867,21 +1025,49 @@ private FileRepository setupRepoForShallowFetch() throws Exception {
 			contentC = r.blob("C");
 			contentD = r.blob("D");
 			contentE = r.blob("E");
-			c1 = bb.commit().add("a", contentA).create();
-			c2 = bb.commit().add("b", contentB).create();
-			c3 = bb.commit().add("c", contentC).create();
-			c4 = bb.commit().add("d", contentD).create();
-			c5 = bb.commit().add("e", contentE).create();
+			c1 = bb.commit().message(commitMessage + " c1").add("a", contentA).create();
+			c2 = bb.commit().message(commitMessage + " c2").add("b", contentB).create();
+			c3 = bb.commit().message(commitMessage + " c3").add("c", contentC).create();
+			c4 = bb.commit().message(commitMessage + " c4").add("d", contentD).create();
+			c5 = bb.commit().message(commitMessage + " c5").add("e", contentE).create();
 			r.getRevWalk().parseHeaders(c5); // fully initialize the tip RevCommit
 			return repo;
 		}
 	}
 
+	private FileRepository setupRepoForUnshallowFetch() throws Exception {
+		String commitMessage = testName.getMethodName();
+		FileRepository repo = createBareRepository();
+		// TestRepository will close the repo, but we need to return an open
+		// one!
+		repo.incrementOpen();
+		try (TestRepository<Repository> r = new TestRepository<>(repo)) {
+			BranchBuilder bb = r.branch("refs/heads/master");
+			contentA = r.blob("A");
+			contentB = r.blob("B");
+			contentC = r.blob("C");
+			contentD = r.blob("D");
+			contentE = r.blob("E");
+			contentF = r.blob("F");
+			contentG = r.blob("G");
+			c1 = bb.commit().message(commitMessage + " c1").add("a", contentA).create();
+			c2 = bb.commit().message(commitMessage + " c2").add("b", contentB).create();
+			c3 = bb.commit().message(commitMessage + " c3").add("c", contentC).create();
+			c4 = bb.commit().message(commitMessage + " c4").add("d", contentD).create();
+			c5 = bb.commit().message(commitMessage + " c5").add("e", contentE).create();
+			c6 = bb.commit().message(commitMessage + " c6").add("f", contentF).create();
+			c7 = bb.commit().message(commitMessage + " c7").add("g", contentF).create();
+			r.getRevWalk().parseHeaders(c7); // fully initialize the tip RevCommit
+
+			return repo;
+		}
+	}
+
 	private static PackIndex writePack(FileRepository repo,
 			Set<? extends ObjectId> want, Set<ObjectIdSet> excludeObjects)
 					throws IOException {
 		try (RevWalk walk = new RevWalk(repo)) {
-			return writePack(repo, walk, 0, want, NONE, excludeObjects);
+			return writePack(repo, walk, 0, want, NONE, excludeObjects, false);
 		}
 	}
 
@@ -892,13 +1078,22 @@ private static PackIndex writeShallowPack(FileRepository repo, int depth,
 		// marked the client's "shallow" commits. Emulate that here.
 		try (DepthWalk.RevWalk walk = new DepthWalk.RevWalk(repo, depth - 1)) {
 			walk.assumeShallow(shallow);
-			return writePack(repo, walk, depth, want, have, EMPTY_ID_SET);
+			return writePack(repo, walk, depth, want, have, EMPTY_ID_SET, false);
+		}
+	}
+
+	private static PackIndex writeUnshallowPack(FileRepository repo, int depth,
+						  Set<? extends ObjectId> want, Set<? extends ObjectId> have) throws IOException {
+		// During negotiation, UploadPack would have set up a DepthWalk and
+		// marked the client's commits in the HAVE as "unshallow". Emulate that here.
+		try (DepthWalk.RevWalk walk = new DepthWalk.RevWalk(repo, depth)) {
+			return writePack(repo, walk, depth, want, have, EMPTY_ID_SET, UNSHALLOW_FETCH);
 		}
 	}
 
 	private static PackIndex writePack(FileRepository repo, RevWalk walk,
 			int depth, Set<? extends ObjectId> want,
-			Set<? extends ObjectId> have, Set<ObjectIdSet> excludeObjects)
+			Set<? extends ObjectId> have, Set<ObjectIdSet> excludeObjects, boolean unshallow)
 					throws IOException {
 		try (PackWriter pw = new PackWriter(repo)) {
 			pw.setDeltaBaseAsOffset(true);
@@ -912,6 +1107,10 @@ private static PackIndex writePack(FileRepository repo, RevWalk walk,
 			// ow doesn't need to be closed; caller closes walk.
 			ObjectWalk ow = walk.toObjectWalkWithSameObjects();
 
+			if (unshallow) {
+				pw.setShallowPack(depth, have);
+			}
+
 			pw.preparePack(NullProgressMonitor.INSTANCE, ow, want, have, NONE);
 			File packdir = repo.getObjectDatabase().getPackDirectory();
 			PackFile packFile = new PackFile(packdir, pw.computeName(),
@@ -1084,8 +1283,8 @@ private void verifyObjectsOrder(ObjectId objectsOrder[]) {
 		for (MutableEntry me : pack) {
 			entries.add(me.cloneEntry());
 		}
-		Collections.sort(entries, (MutableEntry o1, MutableEntry o2) -> Long
-				.signum(o1.getOffset() - o2.getOffset()));
+		entries.sort((MutableEntry o1, MutableEntry o2) -> Long
+			.signum(o1.getOffset() - o2.getOffset()));
 
 		int i = 0;
 		for (MutableEntry me : entries) {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/MultiPackIndexLoaderTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/MultiPackIndexLoaderTest.java
index 494f1d1..3c7e27d 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/MultiPackIndexLoaderTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/MultiPackIndexLoaderTest.java
@@ -17,8 +17,8 @@
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.util.LinkedHashMap;
 import java.util.List;
-import java.util.Map;
 
 import org.eclipse.jgit.internal.storage.file.PackIndex;
 import org.eclipse.jgit.junit.FakeIndexFactory;
@@ -64,8 +64,10 @@ public void load_validFile_basic_jgit() throws Exception {
 				new FakeIndexFactory.IndexObject(
 						"0000000000000000000000000000000000000012", 1502)));
 
-		Map<String, PackIndex> packs = Map.of("p1", idxOne, "p2", idxTwo, "p3",
-				idxThree);
+		LinkedHashMap<String, PackIndex> packs = new LinkedHashMap<>(3);
+		packs.put("p1", idxOne);
+		packs.put("p2", idxTwo);
+		packs.put("p3", idxThree);
 		MultiPackIndexWriter writer = new MultiPackIndexWriter();
 		ByteArrayOutputStream out = new ByteArrayOutputStream();
 		writer.write(NullProgressMonitor.INSTANCE, out, packs);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/MultiPackIndexTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/MultiPackIndexTest.java
index 5de9bd6..6436e31 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/MultiPackIndexTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/MultiPackIndexTest.java
@@ -20,8 +20,8 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.HashSet;
+import java.util.LinkedHashMap;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
 
 import org.eclipse.jgit.internal.storage.file.PackIndex;
@@ -103,8 +103,8 @@ public void basicMidx() throws IOException {
 				new FakeIndexFactory.IndexObject(
 						"0000000000000000000000000000000000000012", 1502)));
 
-		Map<String, PackIndex> packs = Map.of("p1", idxOne, "p2", idxTwo, "p3",
-				idxThree);
+		LinkedHashMap<String, PackIndex> packs = orderedMapOf("p1", idxOne,
+				"p2", idxTwo, "p3", idxThree);
 		MultiPackIndexWriter writer = new MultiPackIndexWriter();
 		ByteArrayOutputStream out = new ByteArrayOutputStream();
 		writer.write(NullProgressMonitor.INSTANCE, out, packs);
@@ -140,7 +140,8 @@ public void jgit_largeOffsetChunk() throws IOException {
 						"0000000000000000000000000000000000000002", (1L << 35)),
 				new FakeIndexFactory.IndexObject(
 						"0000000000000000000000000000000000000003", 13)));
-		Map<String, PackIndex> packs = Map.of("p1", idxOne, "p2", idxTwo);
+		LinkedHashMap<String, PackIndex> packs = orderedMapOf("p1", idxOne,
+				"p2", idxTwo);
 		MultiPackIndexWriter writer = new MultiPackIndexWriter();
 		ByteArrayOutputStream out = new ByteArrayOutputStream();
 		writer.write(NullProgressMonitor.INSTANCE, out, packs);
@@ -170,7 +171,8 @@ public void jgit_largeOffset_noChunk() throws IOException {
 						"0000000000000000000000000000000000000002", 501),
 				new FakeIndexFactory.IndexObject(
 						"0000000000000000000000000000000000000003", 13)));
-		Map<String, PackIndex> packs = Map.of("p1", idxOne, "p2", idxTwo);
+		LinkedHashMap<String, PackIndex> packs = orderedMapOf("p1", idxOne,
+				"p2", idxTwo);
 		MultiPackIndexWriter writer = new MultiPackIndexWriter();
 		ByteArrayOutputStream out = new ByteArrayOutputStream();
 		writer.write(NullProgressMonitor.INSTANCE, out, packs);
@@ -206,7 +208,8 @@ public void jgit_resolve() throws IOException {
 				// Match
 				"32fe829a1c000000000000000000000000000010");
 
-		Map<String, PackIndex> packs = Map.of("p1", idxOne, "p2", idxTwo);
+		LinkedHashMap<String, PackIndex> packs = orderedMapOf("p1", idxOne,
+				"p2", idxTwo);
 		MultiPackIndexWriter writer = new MultiPackIndexWriter();
 		ByteArrayOutputStream out = new ByteArrayOutputStream();
 		writer.write(NullProgressMonitor.INSTANCE, out, packs);
@@ -249,7 +252,8 @@ public void jgit_resolve_matchLimit() throws IOException {
 				// Match
 				"32fe829a1c000000000000000000000000000010");
 
-		Map<String, PackIndex> packs = Map.of("p1", idxOne, "p2", idxTwo);
+		LinkedHashMap<String, PackIndex> packs = orderedMapOf("p1", idxOne,
+				"p2", idxTwo);
 		MultiPackIndexWriter writer = new MultiPackIndexWriter();
 		ByteArrayOutputStream out = new ByteArrayOutputStream();
 		writer.write(NullProgressMonitor.INSTANCE, out, packs);
@@ -283,13 +287,44 @@ public void jgit_resolve_noMatches() throws IOException {
 				"bbbbbb0000000000000000000000000000000003",
 				"32fe829a1c000000000000000000000000000010");
 
-		Map<String, PackIndex> packs = Map.of("p1", idxOne, "p2", idxTwo);
+		LinkedHashMap<String, PackIndex> packs = orderedMapOf("p1", idxOne,
+				"p2", idxTwo);
 		MultiPackIndexWriter writer = new MultiPackIndexWriter();
 		ByteArrayOutputStream out = new ByteArrayOutputStream();
 		writer.write(NullProgressMonitor.INSTANCE, out, packs);
 		MultiPackIndex midx = MultiPackIndexLoader
 				.read(new ByteArrayInputStream(out.toByteArray()));
 
+		Set<ObjectId> results = new HashSet<>();
+		midx.resolve(results, abbrev, 200);
+
+		assertEquals(0, results.size());
+	}
+
+	@Test
+	public void jgit_resolve_noMatches_last() throws IOException {
+		AbbreviatedObjectId abbrev = AbbreviatedObjectId
+				.fromString("dd00000000");
+
+		PackIndex idxOne = indexWith("0000000000000000000000000000000000000001",
+				"3000000000000000000000000000000000000005",
+				"32fe829a1b000000000000000000000000000001",
+				"32fe829a1c000000000000000000000000000001",
+				"32fe829a1c000000000000000000000000000100",
+				"32fe829a1d000000000000000000000000000000");
+		PackIndex idxTwo = indexWith(
+				// Noise
+				"8888880000000000000000000000000000000002",
+				"bbbbbb0000000000000000000000000000000003",
+				"32fe829a1c000000000000000000000000000010");
+
+		LinkedHashMap<String, PackIndex> packs = orderedMapOf("p1", idxOne,
+				"p2", idxTwo);
+		MultiPackIndexWriter writer = new MultiPackIndexWriter();
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		writer.write(NullProgressMonitor.INSTANCE, out, packs);
+		MultiPackIndex midx = MultiPackIndexLoader
+				.read(new ByteArrayInputStream(out.toByteArray()));
 
 		Set<ObjectId> results = new HashSet<>();
 		midx.resolve(results, abbrev, 200);
@@ -305,7 +340,8 @@ public void jgit_resolve_empty() throws IOException {
 		PackIndex idxOne = FakeIndexFactory.indexOf(List.of());
 		PackIndex idxTwo = FakeIndexFactory.indexOf(List.of());
 
-		Map<String, PackIndex> packs = Map.of("p1", idxOne, "p2", idxTwo);
+		LinkedHashMap<String, PackIndex> packs = orderedMapOf("p1", idxOne,
+				"p2", idxTwo);
 		MultiPackIndexWriter writer = new MultiPackIndexWriter();
 		ByteArrayOutputStream out = new ByteArrayOutputStream();
 		writer.write(NullProgressMonitor.INSTANCE, out, packs);
@@ -342,8 +378,8 @@ public void jgit_findPosition() throws IOException {
 				new FakeIndexFactory.IndexObject(
 						"0000000000000000000000000000000000000012", 1502)));
 
-		Map<String, PackIndex> packs = Map.of("p1", idxOne, "p2", idxTwo, "p3",
-				idxThree);
+		LinkedHashMap<String, PackIndex> packs = orderedMapOf("p1", idxOne,
+				"p2", idxTwo, "p3", idxThree);
 		MultiPackIndexWriter writer = new MultiPackIndexWriter();
 		ByteArrayOutputStream out = new ByteArrayOutputStream();
 		writer.write(NullProgressMonitor.INSTANCE, out, packs);
@@ -397,8 +433,8 @@ public void jgit_getObjectCount() throws IOException {
 				new FakeIndexFactory.IndexObject(
 						"0000000000000000000000000000000000000012", 1502)));
 
-		Map<String, PackIndex> packs = Map.of("p1", idxOne, "p2", idxTwo, "p3",
-				idxThree);
+		LinkedHashMap<String, PackIndex> packs = orderedMapOf("p1", idxOne,
+				"p2", idxTwo, "p3", idxThree);
 		MultiPackIndexWriter writer = new MultiPackIndexWriter();
 		ByteArrayOutputStream out = new ByteArrayOutputStream();
 		writer.write(NullProgressMonitor.INSTANCE, out, packs);
@@ -413,7 +449,8 @@ public void jgit_getObjectCount_emtpy() throws IOException {
 		PackIndex idxOne = FakeIndexFactory.indexOf(List.of());
 		PackIndex idxTwo = FakeIndexFactory.indexOf(List.of());
 
-		Map<String, PackIndex> packs = Map.of("p1", idxOne, "p2", idxTwo);
+		LinkedHashMap<String, PackIndex> packs = orderedMapOf("p1", idxOne,
+				"p2", idxTwo);
 		MultiPackIndexWriter writer = new MultiPackIndexWriter();
 		ByteArrayOutputStream out = new ByteArrayOutputStream();
 		writer.write(NullProgressMonitor.INSTANCE, out, packs);
@@ -443,4 +480,21 @@ private static void assertInIndex(MultiPackIndex midx, int expectedPackId,
 				packOffset.getPackId());
 		assertEquals(expectedOffset, packOffset.getOffset());
 	}
+
+	private static LinkedHashMap<String, PackIndex> orderedMapOf(String s1,
+			PackIndex pi1, String s2, PackIndex pi2) {
+		LinkedHashMap<String, PackIndex> map = new LinkedHashMap<>(2);
+		map.put(s1, pi1);
+		map.put(s2, pi2);
+		return map;
+	}
+
+	private static LinkedHashMap<String, PackIndex> orderedMapOf(String s1,
+			PackIndex pi1, String s2, PackIndex pi2, String s3, PackIndex pi3) {
+		LinkedHashMap<String, PackIndex> map = new LinkedHashMap<>(2);
+		map.put(s1, pi1);
+		map.put(s2, pi2);
+		map.put(s3, pi3);
+		return map;
+	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/MultiPackIndexWriterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/MultiPackIndexWriterTest.java
index 8b57a2d..9b085a3 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/MultiPackIndexWriterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/MultiPackIndexWriterTest.java
@@ -22,8 +22,8 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.LinkedHashMap;
 import java.util.List;
-import java.util.Map;
 
 import org.eclipse.jgit.internal.storage.file.PackIndex;
 import org.eclipse.jgit.junit.FakeIndexFactory;
@@ -32,6 +32,7 @@
 import org.eclipse.jgit.util.NB;
 import org.junit.Test;
 
+@SuppressWarnings("boxing")
 public class MultiPackIndexWriterTest {
 
 	@Test
@@ -45,8 +46,9 @@ public void write_allSmallOffsets() throws IOException {
 				object("0000000000000000000000000000000000000004", 1500),
 				object("0000000000000000000000000000000000000006", 3000));
 
-		Map<String, PackIndex> data = Map.of("packname1", index1, "packname2",
-				index2);
+		LinkedHashMap<String, PackIndex> data = new LinkedHashMap<>();
+		data.put("packname1", index1);
+		data.put("packname2", index2);
 
 		MultiPackIndexWriter writer = new MultiPackIndexWriter();
 		ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -74,13 +76,15 @@ public void write_smallOffset_limit() throws IOException {
 		PackIndex index1 = indexOf(
 				object("0000000000000000000000000000000000000001", 500),
 				object("0000000000000000000000000000000000000003", 1500),
-				object("0000000000000000000000000000000000000005", (1L << 32) -1));
+				object("0000000000000000000000000000000000000005",
+						(1L << 32) - 1));
 		PackIndex index2 = indexOf(
 				object("0000000000000000000000000000000000000002", 500),
 				object("0000000000000000000000000000000000000004", 1500),
 				object("0000000000000000000000000000000000000006", 3000));
-		Map<String, PackIndex> data =
-				Map.of("packname1", index1, "packname2", index2);
+		LinkedHashMap<String, PackIndex> data = new LinkedHashMap<>(2);
+		data.put("packname1", index1);
+		data.put("packname2", index2);
 
 		MultiPackIndexWriter writer = new MultiPackIndexWriter();
 		ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -113,12 +117,14 @@ public void write_largeOffset() throws IOException {
 				object("0000000000000000000000000000000000000002", 500),
 				object("0000000000000000000000000000000000000004", 1500),
 				object("0000000000000000000000000000000000000006", 3000));
-		Map<String, PackIndex> data =
-				Map.of("packname1", index1, "packname2", index2);
+		LinkedHashMap<String, PackIndex> data = new LinkedHashMap<>(2);
+		data.put("bbbbbbbbb", index1);
+		data.put("aaaaaaaaa", index2);
 
 		MultiPackIndexWriter writer = new MultiPackIndexWriter();
 		ByteArrayOutputStream out = new ByteArrayOutputStream();
-		writer.write(NullProgressMonitor.INSTANCE, out, data);
+		MultiPackIndexWriter.Result result = writer
+				.write(NullProgressMonitor.INSTANCE, out, data);
 		// header (12 bytes)
 		// + chunkHeader (7 * 12 bytes)
 		// + fanout table (256 * 4 bytes)
@@ -137,13 +143,18 @@ public void write_largeOffset() throws IOException {
 		assertEquals(3, chunkIds.indexOf(MIDX_CHUNKID_LARGEOFFSETS));
 		assertEquals(4, chunkIds.indexOf(MIDX_CHUNKID_REVINDEX));
 		assertEquals(5, chunkIds.indexOf(MIDX_CHUNKID_PACKNAMES));
+
+		assertEquals("bbbbbbbbb", result.packNames().get(0));
+		assertEquals("aaaaaaaaa", result.packNames().get(1));
 	}
 
 	@Test
 	public void jgit_emptyMidx() throws IOException {
 		PackIndex idxOne = FakeIndexFactory.indexOf(List.of());
 		PackIndex idxTwo = FakeIndexFactory.indexOf(List.of());
-		Map<String, PackIndex> packs = Map.of("p1", idxOne, "p2", idxTwo);
+		LinkedHashMap<String, PackIndex> packs = new LinkedHashMap<>(2);
+		packs.put("p1", idxOne);
+		packs.put("p2", idxTwo);
 		MultiPackIndexWriter writer = new MultiPackIndexWriter();
 		ByteArrayOutputStream out = new ByteArrayOutputStream();
 		writer.write(NullProgressMonitor.INSTANCE, out, packs);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/PackIndexMergerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/PackIndexMergerTest.java
index 8218cbc..0eafdaf 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/PackIndexMergerTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/PackIndexMergerTest.java
@@ -15,7 +15,7 @@
 
 import java.util.Arrays;
 import java.util.Iterator;
-import java.util.Map;
+import java.util.LinkedHashMap;
 
 import org.eclipse.jgit.internal.storage.file.PackIndex;
 import org.eclipse.jgit.junit.FakeIndexFactory;
@@ -39,7 +39,7 @@ public void rawIterator_noDuplicates() {
 				oidOffset("0000000000000000000000000000000000000007", 14),
 				oidOffset("0000000000000000000000000000000000000012", 1502));
 		PackIndexMerger merger = new PackIndexMerger(
-				Map.of("p1", idxOne, "p2", idxTwo, "p3", idxThree));
+				orderedMapOf("p1", idxOne, "p2", idxTwo, "p3", idxThree));
 		assertEquals(9, merger.getUniqueObjectCount());
 		assertEquals(3, merger.getPackCount());
 		assertFalse(merger.needsLargeOffsetsChunk());
@@ -60,13 +60,48 @@ public void rawIterator_noDuplicates() {
 	}
 
 	@Test
+	public void rawIterator_noDuplicates_honorPackOrder() {
+		PackIndex idxOne = indexOf(
+				oidOffset("0000000000000000000000000000000000000001", 500),
+				oidOffset("0000000000000000000000000000000000000005", 12),
+				oidOffset("0000000000000000000000000000000000000010", 1500));
+		PackIndex idxTwo = indexOf(
+				oidOffset("0000000000000000000000000000000000000002", 501),
+				oidOffset("0000000000000000000000000000000000000003", 13),
+				oidOffset("0000000000000000000000000000000000000015", 1501));
+		PackIndex idxThree = indexOf(
+				oidOffset("0000000000000000000000000000000000000004", 502),
+				oidOffset("0000000000000000000000000000000000000007", 14),
+				oidOffset("0000000000000000000000000000000000000012", 1502));
+        PackIndexMerger merger = new PackIndexMerger(
+				orderedMapOf("p3", idxThree, "p2", idxTwo, "p1", idxOne));
+		assertEquals(9, merger.getUniqueObjectCount());
+		assertEquals(3, merger.getPackCount());
+		assertFalse(merger.needsLargeOffsetsChunk());
+		Iterator<PackIndexMerger.MidxMutableEntry> it = merger.rawIterator();
+		assertNextEntry(it, "0000000000000000000000000000000000000001", 2, 500);
+		assertNextEntry(it, "0000000000000000000000000000000000000002", 1, 501);
+		assertNextEntry(it, "0000000000000000000000000000000000000003", 1, 13);
+		assertNextEntry(it, "0000000000000000000000000000000000000004", 0, 502);
+		assertNextEntry(it, "0000000000000000000000000000000000000005", 2, 12);
+		assertNextEntry(it, "0000000000000000000000000000000000000007", 0, 14);
+		assertNextEntry(it, "0000000000000000000000000000000000000010", 2,
+				1500);
+		assertNextEntry(it, "0000000000000000000000000000000000000012", 0,
+				1502);
+		assertNextEntry(it, "0000000000000000000000000000000000000015", 1,
+				1501);
+		assertFalse(it.hasNext());
+	}
+
+	@Test
 	public void rawIterator_allDuplicates() {
 		PackIndex idxOne = indexOf(
 				oidOffset("0000000000000000000000000000000000000001", 500),
 				oidOffset("0000000000000000000000000000000000000005", 12),
 				oidOffset("0000000000000000000000000000000000000010", 1500));
 		PackIndexMerger merger = new PackIndexMerger(
-				Map.of("p1", idxOne, "p2", idxOne, "p3", idxOne));
+				orderedMapOf("p1", idxOne, "p2", idxOne, "p3", idxOne));
 		assertEquals(3, merger.getUniqueObjectCount());
 		assertEquals(3, merger.getPackCount());
 		assertFalse(merger.needsLargeOffsetsChunk());
@@ -101,7 +136,7 @@ public void bySha1Iterator_noDuplicates() {
 				oidOffset("0000000000000000000000000000000000000007", 14),
 				oidOffset("0000000000000000000000000000000000000012", 1502));
 		PackIndexMerger merger = new PackIndexMerger(
-				Map.of("p1", idxOne, "p2", idxTwo, "p3", idxThree));
+				orderedMapOf("p1", idxOne, "p2", idxTwo, "p3", idxThree));
 		assertEquals(9, merger.getUniqueObjectCount());
 		assertEquals(3, merger.getPackCount());
 		assertFalse(merger.needsLargeOffsetsChunk());
@@ -128,7 +163,7 @@ public void bySha1Iterator_allDuplicates() {
 				oidOffset("0000000000000000000000000000000000000005", 12),
 				oidOffset("0000000000000000000000000000000000000010", 1500));
 		PackIndexMerger merger = new PackIndexMerger(
-				Map.of("p1", idxOne, "p2", idxOne, "p3", idxOne));
+				orderedMapOf("p1", idxOne, "p2", idxOne, "p3", idxOne));
 		assertEquals(3, merger.getUniqueObjectCount());
 		assertEquals(3, merger.getPackCount());
 		assertFalse(merger.needsLargeOffsetsChunk());
@@ -152,7 +187,7 @@ public void bySha1Iterator_differentIndexSizes() {
 				oidOffset("0000000000000000000000000000000000000007", 12),
 				oidOffset("0000000000000000000000000000000000000012", 1500));
 		PackIndexMerger merger = new PackIndexMerger(
-				Map.of("p1", idxOne, "p2", idxTwo, "p3", idxThree));
+				orderedMapOf("p1", idxOne, "p2", idxTwo, "p3", idxThree));
 		assertEquals(6, merger.getUniqueObjectCount());
 		assertEquals(3, merger.getPackCount());
 		assertFalse(merger.needsLargeOffsetsChunk());
@@ -170,7 +205,7 @@ public void bySha1Iterator_differentIndexSizes() {
 
 	@Test
 	public void merger_noIndexes() {
-		PackIndexMerger merger = new PackIndexMerger(Map.of());
+        PackIndexMerger merger = new PackIndexMerger(new LinkedHashMap<>());
 		assertEquals(0, merger.getUniqueObjectCount());
 		assertFalse(merger.needsLargeOffsetsChunk());
 		assertTrue(merger.getPackNames().isEmpty());
@@ -181,7 +216,7 @@ public void merger_noIndexes() {
 	@Test
 	public void merger_emptyIndexes() {
 		PackIndexMerger merger = new PackIndexMerger(
-				Map.of("p1", indexOf(), "p2", indexOf()));
+                orderedMapOf("p1", indexOf(), "p2", indexOf()));
 		assertEquals(0, merger.getUniqueObjectCount());
 		assertFalse(merger.needsLargeOffsetsChunk());
 		assertEquals(2, merger.getPackNames().size());
@@ -197,7 +232,7 @@ public void bySha1Iterator_largeOffsets_needsChunk() {
 		PackIndex idx2 = indexOf(oidOffset(
 				"0000000000000000000000000000000000000003", (1L << 31) + 10));
 		PackIndexMerger merger = new PackIndexMerger(
-				Map.of("p1", idx1, "p2", idx2));
+                orderedMapOf("p1", idx1, "p2", idx2));
 		assertTrue(merger.needsLargeOffsetsChunk());
 		assertEquals(2, merger.getOffsetsOver31BitsCount());
 		assertEquals(3, merger.getUniqueObjectCount());
@@ -213,7 +248,7 @@ public void bySha1Iterator_largeOffsets_noChunk() {
 		PackIndex idx2 = indexOf(oidOffset(
 				"0000000000000000000000000000000000000003", (1L << 31) + 10));
 		PackIndexMerger merger = new PackIndexMerger(
-				Map.of("p1", idx1, "p2", idx2));
+                orderedMapOf("p1", idx1, "p2", idx2));
 		assertFalse(merger.needsLargeOffsetsChunk());
 		assertEquals(2, merger.getOffsetsOver31BitsCount());
 		assertEquals(3, merger.getUniqueObjectCount());
@@ -236,4 +271,22 @@ private static IndexObject oidOffset(String oid, long offset) {
 	private static PackIndex indexOf(IndexObject... objs) {
 		return FakeIndexFactory.indexOf(Arrays.asList(objs));
 	}
+
+    private static LinkedHashMap<String, PackIndex> orderedMapOf(String s1,
+                                                                 PackIndex pi1, String s2, PackIndex pi2) {
+        LinkedHashMap map = new LinkedHashMap(3);
+        map.put(s1, pi1);
+        map.put(s2, pi2);
+        return map;
+    }
+
+	private static LinkedHashMap<String, PackIndex> orderedMapOf(String s1,
+                                                                 PackIndex pi1, String s2, PackIndex pi2, String s3, PackIndex pi3) {
+        LinkedHashMap map = new LinkedHashMap(3);
+        map.put(s1, pi1);
+        map.put(s2, pi2);
+        map.put(s3, pi3);
+        return map;
+    }
+
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java
index 0fafcd6..d64cfc5 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java
@@ -58,6 +58,7 @@
 import org.eclipse.jgit.util.FS;
 import org.eclipse.jgit.util.FileUtils;
 import org.eclipse.jgit.util.StringUtils;
+import org.eclipse.jgit.util.SystemReader;
 import org.junit.Assume;
 import org.junit.Test;
 
@@ -1982,7 +1983,20 @@ public void testFolderFileAndContentConflicts() throws Exception {
 
 	@Test
 	public void testLongFilename() throws Exception {
-		char[] bytes = new char[253];
+		int maximumFileNameLength = 253;
+		String osName = SystemReader.getInstance().getProperty("os.name");
+		if (osName != null) {
+			switch (osName) {
+			case "NONSTOP_KERNEL":
+				// Safely truncate file names to the maximum supported
+				// by the HPE NonStop OSS file system.
+				maximumFileNameLength = 248;
+				break;
+			default:
+				break;
+			}
+		}
+		char[] bytes = new char[maximumFileNameLength];
 		Arrays.fill(bytes, 'f');
 		String longFileName = new String(bytes);
 		// 1
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateParserTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateParserTest.java
index 5db4563..118ce18 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateParserTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateParserTest.java
@@ -57,6 +57,7 @@
 import java.io.InputStreamReader;
 import java.io.Reader;
 import java.io.StringReader;
+import java.util.List;
 
 import org.eclipse.jgit.errors.PackProtocolException;
 import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
@@ -364,6 +365,77 @@ public void testMissingPusheeField() throws Exception {
 		assertFalse(cert.toText().contains(PushCertificateParser.PUSHEE));
 	}
 
+	@Test
+	public void testWithPushOptions() throws Exception {
+		// Same cert as INPUT, but with push-option lines added after nonce
+		String inputWithOptions = "001ccertificate version 0.1\n"
+				+ "0041pusher Dave Borowitz <dborowitz@google.com> 1433954361 -0700\n"
+				+ "0024pushee git://localhost/repo.git\n"
+				+ "002anonce 1433954361-bde756572d665bba81d8\n"
+				+ "0024push-option label=Code-Review+2\n"
+				+ "0021push-option label=Verified+1\n"
+				+ "0017push-option submit\n"
+				+ "0020push-option hashtag=bug fix\n"
+				+ "0005\n"
+				+ "00680000000000000000000000000000000000000000"
+				+ " 6c2b981a177396fb47345b7df3e4d3f854c6bea7"
+				+ " refs/heads/master\n"
+				+ "0022-----BEGIN PGP SIGNATURE-----\n"
+				+ "0016Version: GnuPG v1\n"
+				+ "0005\n"
+				+ "0045iQEcBAABAgAGBQJVeGg5AAoJEPfTicJkUdPkUggH/RKAeI9/i/LduuiqrL/SSdIa\n"
+				+ "00459tYaSqJKLbXz63M/AW4Sp+4u+dVCQvnAt/a35CVEnpZz6hN4Kn/tiswOWVJf4CO7\n"
+				+ "0045htNubGs5ZMwvD6sLYqKAnrM3WxV/2TbbjzjZW6Jkidz3jz/WRT4SmjGYiEO7aA+V\n"
+				+ "00454ZdIS9f7sW5VsHHYlNThCA7vH8Uu48bUovFXyQlPTX0pToSgrWV3JnTxDNxfn3iG\n"
+				+ "0045IL0zTY/qwVCdXgFownLcs6J050xrrBWIKqfcWr3u4D2aCLyR0v+S/KArr7ulZygY\n"
+				+ "0045+SOklImn8TAZiNxhWtA6ens66IiammUkZYFv7SSzoPLFZT4dC84SmGPWgf94NoQ=\n"
+				+ "000a=XFeC\n"
+				+ "0020-----END PGP SIGNATURE-----\n"
+				+ "0012push-cert-end\n";
+
+		PacketLineIn pckIn = newPacketLineIn(inputWithOptions);
+		PushCertificateParser parser =
+				new PushCertificateParser(db, newEnabledConfig());
+		parser.receiveHeader(pckIn, false);
+
+		parser.addCommand(pckIn.readString());
+		assertEquals(PushCertificateParser.BEGIN_SIGNATURE, pckIn.readString());
+		parser.receiveSignature(pckIn);
+
+		PushCertificate cert = parser.build();
+		assertNotNull(cert);
+		assertEquals("0.1", cert.getVersion());
+		assertEquals("Dave Borowitz", cert.getPusherIdent().getName());
+
+		// Verify push options were parsed and are available on the certificate
+		List<String> pushOptions = cert.getPushOptions();
+		assertEquals(4, pushOptions.size());
+		assertEquals("label=Code-Review+2", pushOptions.get(0));
+		assertEquals("label=Verified+1", pushOptions.get(1));
+		assertEquals("submit", pushOptions.get(2));
+		assertEquals("hashtag=bug fix", pushOptions.get(3));
+
+		// Verify that toText() includes push options (critical for signature verification)
+		String text = cert.toText();
+		assertTrue("toText() must include push-option lines",
+				text.contains("push-option label=Code-Review+2"));
+		assertTrue("toText() must include push-option lines",
+				text.contains("push-option label=Verified+1"));
+		assertTrue("toText() must include push-option lines",
+				text.contains("push-option submit"));
+		assertTrue("toText() must include push-option lines",
+				text.contains("push-option hashtag=bug fix"));
+
+		// Verify push options appear after nonce and before empty line
+		int nonceIndex = text.indexOf("nonce");
+		int pushOptionIndex = text.indexOf("push-option");
+		int emptyLineIndex = text.indexOf("\n\n");
+		assertTrue("push-option must appear after nonce",
+				pushOptionIndex > nonceIndex);
+		assertTrue("push-option must appear before empty line separator",
+				pushOptionIndex < emptyLineIndex);
+	}
+
 	private static String concatPacketLines(String input, int begin, int end)
 			throws IOException {
 		StringBuilder result = new StringBuilder();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReceivePackTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReceivePackTest.java
index 156746d..595b970 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReceivePackTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReceivePackTest.java
@@ -45,8 +45,15 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
 import org.eclipse.jgit.errors.PackProtocolException;
+import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.transport.ReceiveCommand.Result;
 import org.junit.Test;
 
 /** Tests for receive-pack utilities. */
@@ -82,4 +89,62 @@ private void assertParseCommandFails(String input) {
 			// Expected.
 		}
 	}
+
+	@Test
+	public void testCertificatePushOptionsMismatch() throws Exception {
+		ReceiveCommand cmd1 = new ReceiveCommand(
+				ObjectId.fromString("0000000000000000000000000000000000000000"),
+				ObjectId.fromString("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"),
+				"refs/heads/master");
+		ReceiveCommand cmd2 = new ReceiveCommand(
+				ObjectId.fromString("0000000000000000000000000000000000000000"),
+				ObjectId.fromString("badc0ffebadc0ffebadc0ffebadc0ffebadc0ffe"),
+				"refs/heads/branch");
+
+		List<ReceiveCommand> commands = new ArrayList<>();
+		commands.add(cmd1);
+		commands.add(cmd2);
+
+		ReceivePack.validateCertificatePushOptions(
+				Arrays.asList("option1", "option2"),
+				Arrays.asList("option1", "different"), commands);
+
+		assertEquals(Result.REJECTED_OTHER_REASON, cmd1.getResult());
+		assertEquals(Result.REJECTED_OTHER_REASON, cmd2.getResult());
+		assertEquals(JGitText.get().pushCertificateInconsistentPushOptions,
+				cmd1.getMessage());
+		assertEquals(JGitText.get().pushCertificateInconsistentPushOptions,
+				cmd2.getMessage());
+	}
+
+	@Test
+	public void testCertificatePushOptionsMatch() throws Exception {
+		ReceiveCommand cmd = new ReceiveCommand(
+				ObjectId.fromString("0000000000000000000000000000000000000000"),
+				ObjectId.fromString("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"),
+				"refs/heads/master");
+
+		List<ReceiveCommand> commands = Collections.singletonList(cmd);
+
+		ReceivePack.validateCertificatePushOptions(
+				Arrays.asList("option1", "option2"),
+				Arrays.asList("option1", "option2"), commands);
+
+		assertEquals(Result.NOT_ATTEMPTED, cmd.getResult());
+	}
+
+	@Test
+	public void testCertificatePushOptionsNullTreatedAsEmpty() throws Exception {
+		ReceiveCommand cmd = new ReceiveCommand(
+				ObjectId.fromString("0000000000000000000000000000000000000000"),
+				ObjectId.fromString("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"),
+				"refs/heads/master");
+
+		List<ReceiveCommand> commands = Collections.singletonList(cmd);
+
+		ReceivePack.validateCertificatePushOptions(Collections.emptyList(),
+				null, commands);
+
+		assertEquals(Result.NOT_ATTEMPTED, cmd.getResult());
+	}
 }
diff --git a/org.eclipse.jgit.ui/META-INF/MANIFEST.MF b/org.eclipse.jgit.ui/META-INF/MANIFEST.MF
index 498fa2a..72dea20 100644
--- a/org.eclipse.jgit.ui/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ui/META-INF/MANIFEST.MF
@@ -4,15 +4,15 @@
 Bundle-Name: %Bundle-Name
 Automatic-Module-Name: org.eclipse.jgit.ui
 Bundle-SymbolicName: org.eclipse.jgit.ui
-Bundle-Version: 7.4.1.qualifier
+Bundle-Version: 7.5.0.qualifier
 Bundle-Vendor: %Bundle-Vendor
 Bundle-RequiredExecutionEnvironment: JavaSE-17
 Bundle-SCM: url=https://github.com/eclipse-jgit/jgit, connection=scm:git:https://eclipse.gerrithub.io/eclipse-jgit/jgit.git, developerConnection=scm:git:https://eclipse.gerrithub.io/a/eclipse-jgit/jgit.git
-Export-Package: org.eclipse.jgit.awtui;version="7.4.1"
-Import-Package: org.eclipse.jgit.errors;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.lib;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.nls;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.revplot;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.revwalk;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.transport;version="[7.4.1,7.5.0)",
- org.eclipse.jgit.util;version="[7.4.1,7.5.0)"
+Export-Package: org.eclipse.jgit.awtui;version="7.5.0"
+Import-Package: org.eclipse.jgit.errors;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.lib;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.nls;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.revplot;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.revwalk;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.transport;version="[7.5.0,7.6.0)",
+ org.eclipse.jgit.util;version="[7.5.0,7.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 43332a0..dda039e 100644
--- a/org.eclipse.jgit.ui/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.ui/META-INF/SOURCE-MANIFEST.MF
@@ -3,6 +3,6 @@
 Bundle-Name: org.eclipse.jgit.ui - Sources
 Bundle-SymbolicName: org.eclipse.jgit.ui.source
 Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 7.4.1.qualifier
+Bundle-Version: 7.5.0.qualifier
 Bundle-SCM: url=https://github.com/eclipse-jgit/jgit, connection=scm:git:https://eclipse.gerrithub.io/eclipse-jgit/jgit.git, developerConnection=scm:git:https://eclipse.gerrithub.io/a/eclipse-jgit/jgit.git
-Eclipse-SourceBundle: org.eclipse.jgit.ui;version="7.4.1.qualifier";roots="."
+Eclipse-SourceBundle: org.eclipse.jgit.ui;version="7.5.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.ui/pom.xml b/org.eclipse.jgit.ui/pom.xml
index 8fd243e..6b32bf4 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>7.4.1-SNAPSHOT</version>
+    <version>7.5.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.ui</artifactId>
diff --git a/org.eclipse.jgit/.settings/.api_filters b/org.eclipse.jgit/.settings/.api_filters
index eeed75f..7a3b764 100644
--- a/org.eclipse.jgit/.settings/.api_filters
+++ b/org.eclipse.jgit/.settings/.api_filters
@@ -1,73 +1,18 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <component id="org.eclipse.jgit" version="2">
-    <resource path="META-INF/MANIFEST.MF">
-        <filter id="923795461">
+    <resource path="src/org/eclipse/jgit/lib/CoreConfig.java" type="org.eclipse.jgit.lib.CoreConfig">
+        <filter id="336658481">
             <message_arguments>
-                <message_argument value="7.2.2"/>
-                <message_argument value="7.1.0"/>
-            </message_arguments>
-        </filter>
-        <filter id="934281281">
-            <message_arguments>
-                <message_argument value="org.eclipse.jgit.lib"/>
-                <message_argument value="8.1.0"/>
-                <message_argument value="7.1.0"/>
+                <message_argument value="org.eclipse.jgit.lib.CoreConfig"/>
+                <message_argument value="DEFAULT_MULTIPACK_INDEX_ENABLE"/>
             </message_arguments>
         </filter>
     </resource>
-    <resource path="src/org/eclipse/jgit/lib/RefDatabase.java" type="org.eclipse.jgit.lib.RefDatabase">
-        <filter id="336695337">
+    <resource path="src/org/eclipse/jgit/revwalk/RevFlag.java" type="org.eclipse.jgit.revwalk.RevFlag">
+        <filter id="336658481">
             <message_arguments>
-                <message_argument value="org.eclipse.jgit.lib.RefDatabase"/>
-                <message_argument value="getReflogReader(Ref)"/>
-            </message_arguments>
-        </filter>
-    </resource>
-    <resource path="src/org/eclipse/jgit/lib/TypedConfigGetter.java" type="org.eclipse.jgit.lib.TypedConfigGetter">
-        <filter id="403804204">
-            <message_arguments>
-                <message_argument value="org.eclipse.jgit.lib.TypedConfigGetter"/>
-                <message_argument value="getBoolean(Config, String, String, String, Boolean)"/>
-            </message_arguments>
-        </filter>
-        <filter id="403804204">
-            <message_arguments>
-                <message_argument value="org.eclipse.jgit.lib.TypedConfigGetter"/>
-                <message_argument value="getInt(Config, String, String, String, Integer)"/>
-            </message_arguments>
-        </filter>
-        <filter id="403804204">
-            <message_arguments>
-                <message_argument value="org.eclipse.jgit.lib.TypedConfigGetter"/>
-                <message_argument value="getIntInRange(Config, String, String, String, int, int, Integer)"/>
-            </message_arguments>
-        </filter>
-        <filter id="403804204">
-            <message_arguments>
-                <message_argument value="org.eclipse.jgit.lib.TypedConfigGetter"/>
-                <message_argument value="getLong(Config, String, String, String, Long)"/>
-            </message_arguments>
-        </filter>
-        <filter id="403804204">
-            <message_arguments>
-                <message_argument value="org.eclipse.jgit.lib.TypedConfigGetter"/>
-                <message_argument value="getTimeUnit(Config, String, String, String, Long, TimeUnit)"/>
-            </message_arguments>
-        </filter>
-    </resource>
-    <resource path="src/org/eclipse/jgit/revwalk/RevWalk.java" type="org.eclipse.jgit.revwalk.RevWalk">
-        <filter id="1142947843">
-            <message_arguments>
-                <message_argument value="6.10.1"/>
-                <message_argument value="isMergedIntoAnyCommit(RevCommit, Collection&lt;RevCommit&gt;)"/>
-            </message_arguments>
-        </filter>
-    </resource>
-    <resource path="src/org/eclipse/jgit/util/Iterators.java" type="org.eclipse.jgit.util.Iterators">
-        <filter id="1109393411">
-            <message_arguments>
-                <message_argument value="6.10.2"/>
-                <message_argument value="org.eclipse.jgit.util.Iterators"/>
+                <message_argument value="org.eclipse.jgit.revwalk.RevFlag"/>
+                <message_argument value="UNSHALLOW"/>
             </message_arguments>
         </filter>
     </resource>
diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF
index 839d46b..d760003 100644
--- a/org.eclipse.jgit/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@
 Bundle-Name: %Bundle-Name
 Automatic-Module-Name: org.eclipse.jgit
 Bundle-SymbolicName: org.eclipse.jgit
-Bundle-Version: 7.4.1.qualifier
+Bundle-Version: 7.5.0.qualifier
 Bundle-Localization: OSGI-INF/l10n/plugin
 Bundle-Vendor: %Bundle-Vendor
 Bundle-ActivationPolicy: lazy
@@ -11,8 +11,8 @@
 Bundle-SCM: url=https://github.com/eclipse-jgit/jgit, connection=scm:git:https://eclipse.gerrithub.io/eclipse-jgit/jgit.git, developerConnection=scm:git:https://eclipse.gerrithub.io/a/eclipse-jgit/jgit.git
 Service-Component: OSGI-INF/org.eclipse.jgit.internal.util.CleanupService.xml
 Eclipse-ExtensibleAPI: true
-Export-Package: org.eclipse.jgit.annotations;version="7.4.1",
- org.eclipse.jgit.api;version="7.4.1";
+Export-Package: org.eclipse.jgit.annotations;version="7.5.0",
+ org.eclipse.jgit.api;version="7.5.0";
   uses:="org.eclipse.jgit.transport,
    org.eclipse.jgit.notes,
    org.eclipse.jgit.dircache,
@@ -27,21 +27,21 @@
    org.eclipse.jgit.revwalk.filter,
    org.eclipse.jgit.blame,
    org.eclipse.jgit.merge",
- org.eclipse.jgit.api.errors;version="7.4.1";
+ org.eclipse.jgit.api.errors;version="7.5.0";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.errors",
- org.eclipse.jgit.attributes;version="7.4.1";
+ org.eclipse.jgit.attributes;version="7.5.0";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.treewalk",
- org.eclipse.jgit.blame;version="7.4.1";
+ org.eclipse.jgit.blame;version="7.5.0";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.revwalk,
    org.eclipse.jgit.blame.cache,
    org.eclipse.jgit.diff,
    org.eclipse.jgit.treewalk.filter",
- org.eclipse.jgit.blame.cache;version="7.4.1";
+ org.eclipse.jgit.blame.cache;version="7.5.0";
   uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.diff;version="7.4.1";
+ org.eclipse.jgit.diff;version="7.5.0";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.revwalk,
    org.eclipse.jgit.patch,
@@ -49,55 +49,55 @@
    org.eclipse.jgit.treewalk.filter,
    org.eclipse.jgit.treewalk,
    org.eclipse.jgit.util",
- org.eclipse.jgit.dircache;version="7.4.1";
+ org.eclipse.jgit.dircache;version="7.5.0";
   uses:="org.eclipse.jgit.events,
    org.eclipse.jgit.lib,
    org.eclipse.jgit.attributes,
    org.eclipse.jgit.treewalk,
    org.eclipse.jgit.util",
- org.eclipse.jgit.errors;version="7.4.1";
+ org.eclipse.jgit.errors;version="7.5.0";
   uses:="org.eclipse.jgit.transport,
    org.eclipse.jgit.dircache,
    org.eclipse.jgit.lib",
- org.eclipse.jgit.events;version="7.4.1";
+ org.eclipse.jgit.events;version="7.5.0";
   uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.fnmatch;version="7.4.1",
- org.eclipse.jgit.gitrepo;version="7.4.1";
+ org.eclipse.jgit.fnmatch;version="7.5.0",
+ org.eclipse.jgit.gitrepo;version="7.5.0";
   uses:="org.xml.sax.helpers,
    org.eclipse.jgit.api,
    org.eclipse.jgit.api.errors,
    org.eclipse.jgit.lib,
    org.eclipse.jgit.revwalk,
    org.xml.sax",
- org.eclipse.jgit.gitrepo.internal;version="7.4.1";x-internal:=true,
- org.eclipse.jgit.hooks;version="7.4.1";
+ org.eclipse.jgit.gitrepo.internal;version="7.5.0";x-internal:=true,
+ org.eclipse.jgit.hooks;version="7.5.0";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.util",
- org.eclipse.jgit.ignore;version="7.4.1",
- org.eclipse.jgit.ignore.internal;version="7.4.1";
+ org.eclipse.jgit.ignore;version="7.5.0",
+ org.eclipse.jgit.ignore.internal;version="7.5.0";
   x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal;version="7.4.1";
+ org.eclipse.jgit.internal;version="7.5.0";
   x-friends:="org.eclipse.jgit.test,
    org.eclipse.jgit.http.test",
- org.eclipse.jgit.internal.diff;version="7.4.1";
+ org.eclipse.jgit.internal.diff;version="7.5.0";
   x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal.diffmergetool;version="7.4.1";
+ org.eclipse.jgit.internal.diffmergetool;version="7.5.0";
   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="7.4.1";
+ org.eclipse.jgit.internal.fsck;version="7.5.0";
   x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal.revwalk;version="7.4.1";
+ org.eclipse.jgit.internal.revwalk;version="7.5.0";
   x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal.storage.commitgraph;version="7.4.1";
+ org.eclipse.jgit.internal.storage.commitgraph;version="7.5.0";
   x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal.storage.dfs;version="7.4.1";
+ org.eclipse.jgit.internal.storage.dfs;version="7.5.0";
   x-friends:="org.eclipse.jgit.test,
    org.eclipse.jgit.http.server,
    org.eclipse.jgit.http.test,
    org.eclipse.jgit.lfs.test",
- org.eclipse.jgit.internal.storage.file;version="7.4.1";
+ org.eclipse.jgit.internal.storage.file;version="7.5.0";
   x-friends:="org.eclipse.jgit.test,
    org.eclipse.jgit.junit,
    org.eclipse.jgit.junit.http,
@@ -109,37 +109,39 @@
    org.eclipse.jgit.ssh.apache,
    org.eclipse.jgit.ssh.apache.test,
    org.eclipse.jgit.ssh.jsch.test",
- org.eclipse.jgit.internal.storage.io;version="7.4.1";
+ org.eclipse.jgit.internal.storage.io;version="7.5.0";
   x-friends:="org.eclipse.jgit.junit,
    org.eclipse.jgit.test,
    org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.storage.memory;version="7.4.1";
+ org.eclipse.jgit.internal.storage.memory;version="7.5.0";
   x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal.storage.midx;version="7.4.1";x-internal:=true,
- org.eclipse.jgit.internal.storage.pack;version="7.4.1";
+ org.eclipse.jgit.internal.storage.midx;version="7.5.0";
+  x-friends:="org.eclipse.jgit.pgm,
+   org.eclipse.jgit.test",
+ org.eclipse.jgit.internal.storage.pack;version="7.5.0";
   x-friends:="org.eclipse.jgit.junit,
    org.eclipse.jgit.test,
    org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.storage.reftable;version="7.4.1";
+ org.eclipse.jgit.internal.storage.reftable;version="7.5.0";
   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="7.4.1";x-internal:=true,
- org.eclipse.jgit.internal.transport.connectivity;version="7.4.1";
+ org.eclipse.jgit.internal.submodule;version="7.5.0";x-internal:=true,
+ org.eclipse.jgit.internal.transport.connectivity;version="7.5.0";
   x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal.transport.http;version="7.4.1";
+ org.eclipse.jgit.internal.transport.http;version="7.5.0";
   x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal.transport.parser;version="7.4.1";
+ org.eclipse.jgit.internal.transport.parser;version="7.5.0";
   x-friends:="org.eclipse.jgit.http.server,
    org.eclipse.jgit.test",
- org.eclipse.jgit.internal.transport.ssh;version="7.4.1";
+ org.eclipse.jgit.internal.transport.ssh;version="7.5.0";
   x-friends:="org.eclipse.jgit.ssh.apache,
    org.eclipse.jgit.ssh.jsch,
    org.eclipse.jgit.test",
- org.eclipse.jgit.internal.util;version="7.4.1";
+ org.eclipse.jgit.internal.util;version="7.5.0";
   x-friends:="org.eclipse.jgit.junit",
- org.eclipse.jgit.lib;version="7.4.1";
+ org.eclipse.jgit.lib;version="7.5.0";
   uses:="org.eclipse.jgit.transport,
    org.eclipse.jgit.util.sha1,
    org.eclipse.jgit.dircache,
@@ -154,12 +156,12 @@
    org.eclipse.jgit.util,
    org.eclipse.jgit.submodule,
    org.eclipse.jgit.util.time",
- org.eclipse.jgit.lib.internal;version="7.4.1";
+ org.eclipse.jgit.lib.internal;version="7.5.0";
   x-friends:="org.eclipse.jgit.test,
    org.eclipse.jgit.pgm,
    org.eclipse.egit.ui",
- org.eclipse.jgit.logging;version="7.4.1",
- org.eclipse.jgit.merge;version="7.4.1";
+ org.eclipse.jgit.logging;version="7.5.0",
+ org.eclipse.jgit.merge;version="7.5.0";
   uses:="org.eclipse.jgit.dircache,
    org.eclipse.jgit.lib,
    org.eclipse.jgit.revwalk,
@@ -168,42 +170,42 @@
    org.eclipse.jgit.util,
    org.eclipse.jgit.api,
    org.eclipse.jgit.attributes",
- org.eclipse.jgit.nls;version="7.4.1",
- org.eclipse.jgit.notes;version="7.4.1";
+ org.eclipse.jgit.nls;version="7.5.0",
+ org.eclipse.jgit.notes;version="7.5.0";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.revwalk,
    org.eclipse.jgit.treewalk,
    org.eclipse.jgit.merge",
- org.eclipse.jgit.patch;version="7.4.1";
+ org.eclipse.jgit.patch;version="7.5.0";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.revwalk,
    org.eclipse.jgit.diff",
- org.eclipse.jgit.revplot;version="7.4.1";
+ org.eclipse.jgit.revplot;version="7.5.0";
   uses:="org.eclipse.jgit.revwalk,
    org.eclipse.jgit.lib",
- org.eclipse.jgit.revwalk;version="7.4.1";
+ org.eclipse.jgit.revwalk;version="7.5.0";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.revwalk.filter,
    org.eclipse.jgit.diff,
    org.eclipse.jgit.treewalk.filter,
    org.eclipse.jgit.treewalk,
    org.eclipse.jgit.internal.storage.commitgraph",
- org.eclipse.jgit.revwalk.filter;version="7.4.1";
+ org.eclipse.jgit.revwalk.filter;version="7.5.0";
   uses:="org.eclipse.jgit.revwalk,
    org.eclipse.jgit.lib,
    org.eclipse.jgit.util",
- org.eclipse.jgit.storage.file;version="7.4.1";
+ org.eclipse.jgit.storage.file;version="7.5.0";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.util",
- org.eclipse.jgit.storage.pack;version="7.4.1";
+ org.eclipse.jgit.storage.pack;version="7.5.0";
   uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.submodule;version="7.4.1";
+ org.eclipse.jgit.submodule;version="7.5.0";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.treewalk.filter,
    org.eclipse.jgit.diff,
    org.eclipse.jgit.treewalk,
    org.eclipse.jgit.util",
- org.eclipse.jgit.transport;version="7.4.1";
+ org.eclipse.jgit.transport;version="7.5.0";
   uses:="javax.crypto,
    org.eclipse.jgit.hooks,
    org.eclipse.jgit.util.io,
@@ -216,21 +218,21 @@
    org.eclipse.jgit.transport.resolver,
    org.eclipse.jgit.storage.pack,
    org.eclipse.jgit.errors",
- org.eclipse.jgit.transport.http;version="7.4.1";
+ org.eclipse.jgit.transport.http;version="7.5.0";
   uses:="javax.net.ssl",
- org.eclipse.jgit.transport.resolver;version="7.4.1";
+ org.eclipse.jgit.transport.resolver;version="7.5.0";
   uses:="org.eclipse.jgit.transport,
    org.eclipse.jgit.lib",
- org.eclipse.jgit.treewalk;version="7.4.1";
+ org.eclipse.jgit.treewalk;version="7.5.0";
   uses:="org.eclipse.jgit.dircache,
    org.eclipse.jgit.lib,
    org.eclipse.jgit.attributes,
    org.eclipse.jgit.revwalk,
    org.eclipse.jgit.treewalk.filter,
    org.eclipse.jgit.util",
- org.eclipse.jgit.treewalk.filter;version="7.4.1";
+ org.eclipse.jgit.treewalk.filter;version="7.5.0";
   uses:="org.eclipse.jgit.treewalk",
- org.eclipse.jgit.util;version="7.4.1";
+ org.eclipse.jgit.util;version="7.5.0";
   uses:="org.eclipse.jgit.transport,
    org.eclipse.jgit.hooks,
    org.eclipse.jgit.revwalk,
@@ -243,12 +245,12 @@
    org.eclipse.jgit.treewalk,
    javax.net.ssl,
    org.eclipse.jgit.util.time",
- org.eclipse.jgit.util.io;version="7.4.1";
+ org.eclipse.jgit.util.io;version="7.5.0";
   uses:="org.eclipse.jgit.attributes,
    org.eclipse.jgit.lib,
    org.eclipse.jgit.treewalk",
- org.eclipse.jgit.util.sha1;version="7.4.1",
- org.eclipse.jgit.util.time;version="7.4.1"
+ org.eclipse.jgit.util.sha1;version="7.5.0",
+ org.eclipse.jgit.util.time;version="7.5.0"
 Import-Package: com.googlecode.javaewah;version="[1.1.6,2.0.0)",
  javax.crypto,
  javax.management,
diff --git a/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF
index 7637f70..c83ff44 100644
--- a/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF
@@ -3,6 +3,6 @@
 Bundle-Name: org.eclipse.jgit - Sources
 Bundle-SymbolicName: org.eclipse.jgit.source
 Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 7.4.1.qualifier
+Bundle-Version: 7.5.0.qualifier
 Bundle-SCM: url=https://github.com/eclipse-jgit/jgit, connection=scm:git:https://eclipse.gerrithub.io/eclipse-jgit/jgit.git, developerConnection=scm:git:https://eclipse.gerrithub.io/a/eclipse-jgit/jgit.git
-Eclipse-SourceBundle: org.eclipse.jgit;version="7.4.1.qualifier";roots="."
+Eclipse-SourceBundle: org.eclipse.jgit;version="7.5.0.qualifier";roots="."
diff --git a/org.eclipse.jgit/pom.xml b/org.eclipse.jgit/pom.xml
index 286d41e..9213e7a 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>7.4.1-SNAPSHOT</version>
+    <version>7.5.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit</artifactId>
diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
index e24cba6..6158032 100644
--- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
+++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
@@ -638,6 +638,7 @@
 pushCertificateInvalidFieldValue=Push certificate has missing or invalid value for {0}: {1}
 pushCertificateInvalidHeader=Push certificate has invalid header format
 pushCertificateInvalidSignature=Push certificate has invalid signature format
+pushCertificateInconsistentPushOptions=Push certificate push options do not match command push options
 pushDefaultNothing=No refspec given and push.default=nothing; no upstream branch can be determined
 pushDefaultNoUpstream=No upstream branch found for local branch ''{0}''
 pushDefaultSimple=push.default=simple requires local branch name ''{0}'' to be equal to upstream tracked branch name ''{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 8928f47..9bd99ed 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
@@ -669,6 +669,7 @@ public static JGitText get() {
 	/***/ public String pushCertificateInvalidFieldValue;
 	/***/ public String pushCertificateInvalidHeader;
 	/***/ public String pushCertificateInvalidSignature;
+	/***/ public String pushCertificateInconsistentPushOptions;
 	/***/ public String pushDefaultNothing;
 	/***/ public String pushDefaultNoUpstream;
 	/***/ public String pushDefaultSimple;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlock.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlock.java
index 16f80bc..8f2db71 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlock.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlock.java
@@ -21,7 +21,7 @@
 import org.eclipse.jgit.internal.storage.pack.PackOutputStream;
 
 /** A cached slice of a {@link BlockBasedFile}. */
-final class DfsBlock {
+sealed class DfsBlock permits DfsBlockMidx {
 	final DfsStreamKey stream;
 	final long start;
 	final long end;
@@ -34,6 +34,14 @@ final class DfsBlock {
 		block = buf;
 	}
 
+	protected DfsBlock(DfsBlock other) {
+		stream = other.stream;
+		start = other.start;
+		end = other.end;
+		// Subclass overrides methods using block
+		block = null;
+	}
+
 	int size() {
 		return block.length;
 	}
@@ -53,7 +61,7 @@ int copy(long pos, byte[] dstbuf, int dstoff, int cnt) {
 		return copy(ptr, dstbuf, dstoff, cnt);
 	}
 
-	int copy(int p, byte[] b, int o, int n) {
+	private int copy(int p, byte[] b, int o, int n) {
 		n = Math.min(block.length - p, n);
 		System.arraycopy(block, p, b, o, n);
 		return n;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockMidx.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockMidx.java
new file mode 100644
index 0000000..f3bdc17
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockMidx.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2025, 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
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.storage.dfs;
+
+import org.eclipse.jgit.internal.storage.pack.PackOutputStream;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.zip.CRC32;
+import java.util.zip.DataFormatException;
+import java.util.zip.Inflater;
+
+/**
+ * DfsBlock where offsets come from the multipack index
+ * <p>
+ * This is the DfsBlock returned by the multipack index. It wraps the block of
+ * the real file translating offsets from midx to pack.
+ */
+final class DfsBlockMidx extends DfsBlock {
+	private final DfsBlock src;
+
+	private final long midxPackStart;
+
+	/**
+	 * Map midx offsets to a real pack block
+	 *
+	 * @param packBlock
+	 *            the real pack block
+	 * @param midxPackStart
+	 *            midx offset where the pack (of this block) starts
+	 */
+	DfsBlockMidx(DfsBlock packBlock, long midxPackStart) {
+		// Use start/end/key from the pack block
+		super(packBlock);
+		this.src = packBlock;
+		this.midxPackStart = midxPackStart;
+	}
+
+	@Override
+	int size() {
+		return src.size();
+	}
+
+	@Override
+	ByteBuffer zeroCopyByteBuffer(int n) {
+		return src.zeroCopyByteBuffer(n);
+	}
+
+	@Override
+	boolean contains(DfsStreamKey want, long pos) {
+		return src.contains(want, pos - midxPackStart);
+	}
+
+	@Override
+	int copy(long pos, byte[] dstbuf, int dstoff, int cnt) {
+		return src.copy(pos - midxPackStart, dstbuf, dstoff, cnt);
+	}
+
+	@Override
+	int setInput(long pos, Inflater inf) throws DataFormatException {
+		return src.setInput(pos - midxPackStart, inf);
+	}
+
+	@Override
+	void crc32(CRC32 out, long pos, int cnt) {
+		src.crc32(out, pos - midxPackStart, cnt);
+	}
+
+	@Override
+	void write(PackOutputStream out, long pos, int cnt) throws IOException {
+		src.write(out, pos - midxPackStart, cnt);
+	}
+
+	@Override
+	void check(Inflater inf, byte[] tmp, long pos, int cnt)
+			throws DataFormatException {
+		src.check(inf, tmp, pos - midxPackStart, cnt);
+	}
+}
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 199481c..78d65b5 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.COMMIT_GRAPH;
+import static org.eclipse.jgit.internal.storage.pack.PackExt.MULTI_PACK_INDEX;
 import static org.eclipse.jgit.internal.storage.pack.PackExt.OBJECT_SIZE_INDEX;
 import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
 import static org.eclipse.jgit.internal.storage.pack.PackExt.REFTABLE;
@@ -25,6 +26,7 @@
 
 import java.io.IOException;
 import java.time.Instant;
+import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Calendar;
@@ -33,6 +35,7 @@
 import java.util.GregorianCalendar;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Queue;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 
@@ -95,6 +98,8 @@ public class DfsGarbageCollector {
 	private List<DfsReftable> reftablesBefore;
 	private List<DfsPackFile> expiredGarbagePacks;
 
+	private List<DfsPackFileMidx> existingMidxs;
+
 	private Collection<Ref> refsBefore;
 	private Set<ObjectId> allHeadsAndTags;
 	private Set<ObjectId> allTags;
@@ -431,9 +436,11 @@ private Collection<Ref> getAllRefs() throws IOException {
 	}
 
 	private void readPacksBefore() throws IOException {
-		DfsPackFile[] packs = objdb.getPacks();
-		packsBefore = new ArrayList<>(packs.length);
-		expiredGarbagePacks = new ArrayList<>(packs.length);
+		DfsPackFile[] rawPacks = objdb.getPacks();
+		List<DfsPackFile> packs = getPlainPacks(rawPacks);
+		existingMidxs = getMidxPacks(rawPacks);
+		packsBefore = new ArrayList<>(packs.size());
+		expiredGarbagePacks = new ArrayList<>(packs.size());
 
 		long now = SystemReader.getInstance().now().toEpochMilli();
 		for (DfsPackFile p : packs) {
@@ -448,6 +455,41 @@ private void readPacksBefore() throws IOException {
 		}
 	}
 
+	private static List<DfsPackFile> getPlainPacks(DfsPackFile[] packs) {
+		List<DfsPackFile> plainPacks = new ArrayList<>();
+		Queue<DfsPackFile> pending = new ArrayDeque<>(
+				Arrays.stream(packs).toList());
+		while (!pending.isEmpty()) {
+			DfsPackFile pack = pending.poll();
+			if (pack instanceof DfsPackFileMidx midxPack) {
+				plainPacks.addAll(midxPack.getCoveredPacks());
+				if (midxPack.getMultipackIndexBase() != null) {
+					pending.add(midxPack.getMultipackIndexBase());
+				}
+			} else {
+				plainPacks.add(pack);
+			}
+		}
+		return plainPacks;
+	}
+
+	private static List<DfsPackFileMidx> getMidxPacks(DfsPackFile[] packs) {
+		List<DfsPackFileMidx> topLevelMidxs = Arrays.stream(packs).filter(
+				p -> p.getPackDescription().hasFileExt(MULTI_PACK_INDEX))
+				.map(p -> (DfsPackFileMidx) p).toList();
+
+		List<DfsPackFileMidx> midxPacks = new ArrayList<>();
+		Queue<DfsPackFileMidx> pending = new ArrayDeque<>(topLevelMidxs);
+		while (!pending.isEmpty()) {
+			DfsPackFileMidx midx = pending.poll();
+			midxPacks.add(midx);
+			if (midx.getMultipackIndexBase() != null) {
+				pending.add(midx.getMultipackIndexBase());
+			}
+		}
+		return midxPacks;
+	}
+
 	private void readReftablesBefore() throws IOException {
 		DfsReftable[] tables = objdb.getReftables();
 		reftablesBefore = new ArrayList<>(Arrays.asList(tables));
@@ -568,6 +610,11 @@ private Set<DfsPackDescription> toPrune() {
 		for (DfsPackFile pack : expiredGarbagePacks) {
 			toPrune.add(pack.getPackDescription());
 		}
+
+		for (DfsPackFileMidx pack : existingMidxs) {
+			toPrune.add(pack.getPackDescription());
+		}
+
 		return toPrune;
 	}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsMidxWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsMidxWriter.java
new file mode 100644
index 0000000..28ef98d
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsMidxWriter.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2025, 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.dfs;
+
+import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toMap;
+import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.GC;
+import static org.eclipse.jgit.internal.storage.pack.PackExt.MULTI_PACK_INDEX;
+
+import java.io.IOException;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.internal.storage.file.PackIndex;
+import org.eclipse.jgit.internal.storage.midx.MultiPackIndexWriter;
+import org.eclipse.jgit.lib.ProgressMonitor;
+
+/**
+ * Create a pack with a multipack index, setting the required fields in the
+ * description.
+ */
+public class DfsMidxWriter {
+
+	private DfsMidxWriter() {
+	}
+
+	/**
+	 * Create a pack with the multipack index
+	 *
+	 * @param pm
+	 *            a progress monitor
+	 * @param objdb
+	 *            an object database
+	 * @param packs
+	 *            the packs to cover
+	 * @param base
+	 *            parent of this midx in the chain (if any).
+	 * @return a pack (uncommitted) with the multipack index of the packs passed
+	 *         as parameter.
+	 * @throws IOException
+	 *             an error opening the packs or writing the stream.
+	 */
+	public static DfsPackDescription writeMidx(ProgressMonitor pm,
+			DfsObjDatabase objdb, List<DfsPackFile> packs,
+			@Nullable DfsPackDescription base) throws IOException {
+		LinkedHashMap<String, PackIndex> inputs = new LinkedHashMap<>(
+				packs.size());
+		try (DfsReader ctx = objdb.newReader()) {
+			for (DfsPackFile pack : packs) {
+				inputs.put(pack.getPackDescription().getPackName(),
+						pack.getPackIndex(ctx));
+			}
+		}
+
+		DfsPackDescription midxPackDesc = objdb.newPack(GC);
+		try (DfsOutputStream out = objdb.writeFile(midxPackDesc,
+				MULTI_PACK_INDEX)) {
+			MultiPackIndexWriter w = new MultiPackIndexWriter();
+			MultiPackIndexWriter.Result result = w.write(pm, out, inputs);
+			midxPackDesc.addFileExt(MULTI_PACK_INDEX);
+			midxPackDesc.setObjectCount(result.objectCount());
+
+			Map<String, DfsPackDescription> byName = packs.stream()
+					.map(DfsPackFile::getPackDescription)
+					.collect(toMap(DfsPackDescription::getPackName,
+							Function.identity()));
+			List<DfsPackDescription> coveredPacks = result.packNames().stream()
+					.map(byName::get).collect(toList());
+			midxPackDesc.setCoveredPacks(coveredPacks);
+			if (base != null) {
+				midxPackDesc.setMultiPackIndexBase(base);
+			}
+		}
+
+		return midxPackDesc;
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjDatabase.java
index 1a873d1..7b7a378 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjDatabase.java
@@ -13,13 +13,14 @@
 import static java.util.stream.Collectors.joining;
 import static org.eclipse.jgit.internal.storage.pack.PackExt.BITMAP_INDEX;
 import static org.eclipse.jgit.internal.storage.pack.PackExt.INDEX;
+import static org.eclipse.jgit.internal.storage.pack.PackExt.MULTI_PACK_INDEX;
+import static org.eclipse.jgit.internal.storage.pack.PackExt.REFTABLE;
 
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -27,13 +28,15 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.Collectors;
 
 import org.eclipse.jgit.internal.storage.file.BasePackIndexWriter;
 import org.eclipse.jgit.internal.storage.file.PackBitmapIndexWriterV1;
-import org.eclipse.jgit.internal.storage.pack.PackIndexWriter;
 import org.eclipse.jgit.internal.storage.pack.PackBitmapIndexWriter;
 import org.eclipse.jgit.internal.storage.pack.PackExt;
+import org.eclipse.jgit.internal.storage.pack.PackIndexWriter;
 import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.ConfigConstants;
 import org.eclipse.jgit.lib.ObjectDatabase;
 import org.eclipse.jgit.lib.ObjectInserter;
 import org.eclipse.jgit.lib.ObjectReader;
@@ -198,6 +201,8 @@ public String toString() {
 
 	private Comparator<DfsPackDescription> packComparator;
 
+	private boolean useMultipackIndex;
+
 	/**
 	 * Initialize an object database for our repository.
 	 *
@@ -212,6 +217,9 @@ protected DfsObjDatabase(DfsRepository repository,
 		this.packList = new AtomicReference<>(NO_PACKS);
 		this.readerOptions = options;
 		this.packComparator = DfsPackDescription.objectLookupComparator();
+		this.useMultipackIndex = repository.getConfig().getBoolean(
+				ConfigConstants.CONFIG_CORE_SECTION,
+				ConfigConstants.CONFIG_KEY_MULTIPACKINDEX, false);
 	}
 
 	/**
@@ -249,6 +257,28 @@ public ObjectInserter newInserter() {
 	}
 
 	/**
+	 * Whether to use multipack indexes in the list of packs
+	 * <p>
+	 * This overrides the value in the core.multiPackIndex config key read at
+	 * construction time.
+	 *
+	 * @param value
+	 *            value to set in the flag
+	 */
+	public void setUseMultipackIndex(boolean value) {
+		this.useMultipackIndex = value;
+	}
+
+	/**
+	 * Should this object db return multipack indexes
+	 *
+	 * @return current value of the flag
+	 */
+	protected boolean useMultipackIndex() {
+		return this.useMultipackIndex;
+	}
+
+	/**
 	 * Scan and list all available pack files in the repository.
 	 *
 	 * @return list of available packs. The returned array is shared with the
@@ -455,6 +485,11 @@ protected abstract void commitPackImpl(Collection<DfsPackDescription> desc,
 	 * The returned list must support random access and must be mutable by the
 	 * caller. It is sorted in place using the natural sorting of the returned
 	 * DfsPackDescription objects.
+	 * <p>
+	 * With multipack index enabled, this method returns the descriptions of the
+	 * multipack index(es) (which include descriptions of covered packs) and of
+	 * packs not covered by the midxs. With multipack index disabled, it returns
+	 * only regular packs (the midx is just ignored).
 	 *
 	 * @return available packs. May be empty if there are no packs.
 	 * @throws java.io.IOException
@@ -579,10 +614,10 @@ private PackList scanPacksImpl(PackList old) throws IOException {
 		Map<DfsPackDescription, DfsReftable> reftables = reftableMap(old);
 
 		List<DfsPackDescription> scanned = listPacks();
-		Collections.sort(scanned, packComparator);
+		scanned.sort(packComparator);
 
 		List<DfsPackFile> newPacks = new ArrayList<>(scanned.size());
-		List<DfsReftable> newReftables = new ArrayList<>(scanned.size());
+		List<DfsPackDescription> packsWithReftables = new ArrayList<>();
 		boolean foundNew = false;
 		for (DfsPackDescription dsc : scanned) {
 			DfsPackFile oldPack = packs.remove(dsc);
@@ -591,8 +626,20 @@ private PackList scanPacksImpl(PackList old) throws IOException {
 			} else if (dsc.hasFileExt(PackExt.PACK)) {
 				newPacks.add(createDfsPackFile(cache, dsc));
 				foundNew = true;
+			} else if (dsc.hasFileExt(MULTI_PACK_INDEX)) {
+				newPacks.add(
+						createDfsPackFileMidx(cache, dsc, packsWithReftables));
+				foundNew = true;
 			}
 
+			if (dsc.hasFileExt(REFTABLE)) {
+				packsWithReftables.add(dsc);
+			}
+		}
+
+		List<DfsReftable> newReftables = new ArrayList<>(
+				packsWithReftables.size());
+		for (DfsPackDescription dsc : packsWithReftables) {
 			DfsReftable oldReftable = reftables.remove(dsc);
 			if (oldReftable != null) {
 				newReftables.add(oldReftable);
@@ -607,7 +654,7 @@ private PackList scanPacksImpl(PackList old) throws IOException {
 		if (!foundNew) {
 			return old;
 		}
-		Collections.sort(newReftables, reftableComparator());
+		newReftables.sort(reftableComparator());
 		return new PackList(
 				newPacks.toArray(new DfsPackFile[0]),
 				newReftables.toArray(new DfsReftable[0]));
@@ -615,7 +662,7 @@ private PackList scanPacksImpl(PackList old) throws IOException {
 
 	/**
 	 * Create instances of DfsPackFile
-	 *
+	 * <p>
 	 * Implementors can decide to construct or wrap DfsPackFile in different
 	 * ways.
 	 *
@@ -630,6 +677,38 @@ protected DfsPackFile createDfsPackFile(DfsBlockCache cache,
 		return new DfsPackFile(cache, dsc);
 	}
 
+	/**
+	 * Create instances of DfsPackFileMidx
+	 *
+	 * @param cache
+	 *            block cache
+	 * @param dsc
+	 *            pack description
+	 * @param containsReftables
+	 *            used to return packs inside this midx that also have reftables
+	 *
+	 * @return the dfs packfile
+	 */
+	protected DfsPackFileMidx createDfsPackFileMidx(DfsBlockCache cache,
+			DfsPackDescription dsc,
+			List<DfsPackDescription> containsReftables) {
+		DfsPackFileMidx base = null;
+		if (dsc.getMultiPackIndexBase() != null) {
+			// The base is always a multipack index
+			base = createDfsPackFileMidx(cache, dsc.getMultiPackIndexBase(),
+					containsReftables);
+		}
+		// A pack shouldn't be in the pack list and inside a multipack index
+		// at the same time. In that case, we will have it under two
+		// different DfsPackFile instances.
+		List<DfsPackFile> coveredPacks = dsc.getCoveredPacks().stream()
+				.map(desc -> createDfsPackFile(cache, desc))
+				.collect(Collectors.toUnmodifiableList());
+		dsc.getCoveredPacks().stream().filter(d -> d.hasFileExt(REFTABLE))
+				.forEach(containsReftables::add);
+		return DfsPackFileMidx.create(cache, dsc, coveredPacks, base);
+	}
+
 	private static Map<DfsPackDescription, DfsPackFile> packMap(PackList old) {
 		Map<DfsPackDescription, DfsPackFile> forReuse = new HashMap<>();
 		for (DfsPackFile p : old.packs) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackCompactor.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackCompactor.java
index 6339b03..8860cce 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackCompactor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackCompactor.java
@@ -11,7 +11,6 @@
 package org.eclipse.jgit.internal.storage.dfs;
 
 import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.COMPACT;
-import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.GC;
 import static org.eclipse.jgit.internal.storage.pack.PackExt.OBJECT_SIZE_INDEX;
 import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
 import static org.eclipse.jgit.internal.storage.pack.PackExt.REFTABLE;
@@ -68,10 +67,11 @@ public class DfsPackCompactor {
 	private final List<DfsReftable> srcReftables;
 	private final List<ObjectIdSet> exclude;
 
+	private final List<DfsPackDescription> prune;
+
 	private PackStatistics newStats;
 	private DfsPackDescription outDesc;
 
-	private int autoAddSize;
 	private ReftableConfig reftableConfig;
 
 	private RevWalk rw;
@@ -86,10 +86,10 @@ public class DfsPackCompactor {
 	 */
 	public DfsPackCompactor(DfsRepository repository) {
 		repo = repository;
-		autoAddSize = 5 * 1024 * 1024; // 5 MiB
 		srcPacks = new ArrayList<>();
 		srcReftables = new ArrayList<>();
 		exclude = new ArrayList<>(4);
+		prune = new ArrayList<>();
 	}
 
 	/**
@@ -135,38 +135,6 @@ public DfsPackCompactor add(DfsReftable table) {
 	}
 
 	/**
-	 * Automatically select pack and reftables to be included, and add them.
-	 * <p>
-	 * Packs are selected based on size, smaller packs get included while bigger
-	 * ones are omitted.
-	 *
-	 * @return {@code this}
-	 * @throws java.io.IOException
-	 *             existing packs cannot be read.
-	 */
-	public DfsPackCompactor autoAdd() throws IOException {
-		DfsObjDatabase objdb = repo.getObjectDatabase();
-		for (DfsPackFile pack : objdb.getPacks()) {
-			DfsPackDescription d = pack.getPackDescription();
-			if (d.getFileSize(PACK) < autoAddSize)
-				add(pack);
-			else
-				exclude(pack);
-		}
-
-		if (reftableConfig != null) {
-			for (DfsReftable table : objdb.getReftables()) {
-				DfsPackDescription d = table.getPackDescription();
-				if (d.getPackSource() != GC
-						&& d.getFileSize(REFTABLE) < autoAddSize) {
-					add(table);
-				}
-			}
-		}
-		return this;
-	}
-
-	/**
 	 * Exclude objects from the compacted pack.
 	 *
 	 * @param set
@@ -188,11 +156,23 @@ public DfsPackCompactor exclude(ObjectIdSet set) {
 	 *             pack index cannot be loaded.
 	 */
 	public DfsPackCompactor exclude(DfsPackFile pack) throws IOException {
-		final PackIndex idx;
+		final ObjectIdSet objectIdSet;
 		try (DfsReader ctx = (DfsReader) repo.newObjectReader()) {
-			idx = pack.getPackIndex(ctx);
+			objectIdSet = pack.asObjectIdSet(ctx);
 		}
-		return exclude(idx);
+		return exclude(objectIdSet);
+	}
+
+	/**
+	 * Delete also this pack when writing to the db the compacted packs
+	 *
+	 * @param pack
+	 *            a pack to delete
+	 * @return {@code this}
+	 */
+	public DfsPackCompactor prune(DfsPackFile pack) {
+		prune.add(pack.getPackDescription());
+		return this;
 	}
 
 	/**
@@ -367,6 +347,7 @@ private Collection<DfsPackDescription> toPrune() {
 		Set<DfsPackDescription> toPrune = new HashSet<>();
 		toPrune.addAll(packs);
 		toPrune.addAll(reftables);
+		toPrune.addAll(prune);
 		return toPrune;
 	}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackDescription.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackDescription.java
index 663190a..d4fe566 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackDescription.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackDescription.java
@@ -13,10 +13,14 @@
 import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
 import static org.eclipse.jgit.internal.storage.pack.PackExt.REFTABLE;
 
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.Comparator;
+import java.util.List;
 
 import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.annotations.Nullable;
 import org.eclipse.jgit.internal.storage.commitgraph.CommitGraphWriter;
 import org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource;
 import org.eclipse.jgit.internal.storage.pack.PackExt;
@@ -150,6 +154,11 @@ static Comparator<DfsPackDescription> reuseComparator() {
 	private int indexVersion;
 	private long estimatedPackSize;
 
+	// Packs required by this pack (because it is e.g. a multipack index)
+	private List<? extends DfsPackDescription> coveredPacks = Collections.EMPTY_LIST;
+
+	private DfsPackDescription multiPackIndexBase;
+
 	/**
 	 * Initialize a description by pack name and repository.
 	 * <p>
@@ -210,6 +219,15 @@ public boolean hasFileExt(PackExt ext) {
 	}
 
 	/**
+	 * The basic pack name (no extension) for this pack
+	 *
+	 * @return pack name
+	 */
+	public String getPackName() {
+		return packName;
+	}
+
+	/**
 	 * Get file name
 	 *
 	 * @param ext
@@ -527,6 +545,52 @@ public DfsPackDescription setIndexVersion(int version) {
 		return this;
 	}
 
+	/**
+	 * Packs "included" in this pack (e.g because this is a multi-pack index)
+	 *
+	 * @return descriptions of the packs which are included in this one. Empty
+	 *         list if none. Never null.
+	 */
+	@NonNull
+	public List<DfsPackDescription> getCoveredPacks() {
+		return new ArrayList<>(coveredPacks);
+	}
+
+	/**
+	 * Set the list of packs required by this one (usually for multi-pack index)
+	 *
+	 * @param coveredPacks
+	 *            list of pack names without extension
+	 */
+	public void setCoveredPacks(
+			List<? extends DfsPackDescription> coveredPacks) {
+		if (coveredPacks == null) {
+			throw new IllegalStateException(
+					"Cannot set to null coveredPacks. Use empty list if needed."); //$NON-NLS-1$
+		}
+		this.coveredPacks = coveredPacks;
+	}
+
+	/**
+	 * The base if this multipack index
+	 *
+	 * @return the base of this multipack index
+	 */
+	@Nullable
+	public DfsPackDescription getMultiPackIndexBase() {
+		return multiPackIndexBase;
+	}
+
+	/**
+	 * Set the base of this multipack index
+	 *
+	 * @param desc
+	 *            pack with the multipack index preceding this one
+	 */
+	public void setMultiPackIndexBase(DfsPackDescription desc) {
+		this.multiPackIndexBase = desc;
+	}
+
 	@Override
 	public int hashCode() {
 		return packName.hashCode();
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 05b63ea..ecc97e9 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
@@ -37,6 +37,7 @@
 import java.util.zip.DataFormatException;
 import java.util.zip.Inflater;
 
+import org.eclipse.jgit.annotations.NonNull;
 import org.eclipse.jgit.errors.CorruptObjectException;
 import org.eclipse.jgit.errors.LargeObjectException;
 import org.eclipse.jgit.errors.MissingObjectException;
@@ -57,9 +58,11 @@
 import org.eclipse.jgit.internal.storage.pack.StoredObjectRepresentation;
 import org.eclipse.jgit.lib.AbbreviatedObjectId;
 import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.BitmapIndex;
 import org.eclipse.jgit.lib.ConfigConstants;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectIdSet;
 import org.eclipse.jgit.lib.ObjectLoader;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.lib.StoredConfig;
@@ -71,12 +74,15 @@
  * delta packed format yielding high compression of lots of object where some
  * objects are similar.
  */
-public final class DfsPackFile extends BlockBasedFile {
+public sealed class DfsPackFile extends BlockBasedFile permits DfsPackFileMidx {
 	private static final int REC_SIZE = Constants.OBJECT_ID_LENGTH + 8;
 
 	private static final long REF_POSITION = 0;
 
-	private static final Comparator<DfsObjectToPack> OFFSET_SORT = (
+	/**
+	 * Order objects by offset
+	 */
+	protected static final Comparator<DfsObjectToPack> OFFSET_SORT = (
 			DfsObjectToPack a,
 			DfsObjectToPack b) -> Long.signum(a.getOffset() - b.getOffset());
 
@@ -248,6 +254,22 @@ public PackIndex getPackIndex(DfsReader ctx) throws IOException {
 		return idx(ctx);
 	}
 
+	/**
+	 * Get a view of this packfile as a set of objects
+	 * <p>
+	 * To use when the caller only needs to check inclusion (without specific
+	 * order or getting offsets).
+	 *
+	 * @param ctx
+	 *            reader context
+	 * @return a view of this packfile as a set of objects
+	 * @throws IOException
+	 *             cannot load the backing data from storage
+	 */
+	public ObjectIdSet asObjectIdSet(DfsReader ctx) throws IOException {
+		return idx(ctx);
+	}
+
 	private PackIndex idx(DfsReader ctx) throws IOException {
 		if (index != null) {
 			return index;
@@ -1201,6 +1223,34 @@ long getIndexedObjectSize(DfsReader ctx, int idxPosition)
 	}
 
 	/**
+	 * Return pack(s) with all its objects included in the bitmap.
+	 * <p>
+	 * The bitmap is modified removing the objects in the returned packs.
+	 * <p>
+	 * The list could contain more than one pack in the multipack-index case.
+	 *
+	 * @param ctx
+	 *            a reader
+	 * @param need
+	 *            bitmap with the required objects. It can be modified in this
+	 *            call. The objects from the returned packs are removed from the
+	 *            bitmap.
+	 * @return list of packs with ALL their objects in the bitmap.
+	 * @throws IOException
+	 *             error reading the bitmap index.
+	 */
+	@NonNull
+	List<DfsPackFile> fullyIncludedIn(DfsReader ctx,
+			BitmapIndex.BitmapBuilder need) throws IOException {
+		PackBitmapIndex bi = this.getBitmapIndex(ctx);
+		if (bi != null && need.removeAllOrNone(bi)) {
+			return Collections.singletonList(this);
+		}
+
+		return Collections.emptyList();
+	}
+
+	/**
 	 * Populates the representation object with the details of how the object at
 	 * "pos" is stored in this pack (e.g. whole or deltified, its packed
 	 * length).
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFileMidx.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFileMidx.java
new file mode 100644
index 0000000..742fe6c
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFileMidx.java
@@ -0,0 +1,610 @@
+/*
+ * Copyright (C) 2025, 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.dfs;
+
+import static org.eclipse.jgit.internal.storage.pack.PackExt.MULTI_PACK_INDEX;
+import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
+
+import java.io.IOException;
+import java.nio.channels.Channels;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.zip.DataFormatException;
+
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.errors.StoredObjectRepresentationNotAvailableException;
+import org.eclipse.jgit.internal.storage.commitgraph.CommitGraph;
+import org.eclipse.jgit.internal.storage.file.PackBitmapIndex;
+import org.eclipse.jgit.internal.storage.file.PackIndex;
+import org.eclipse.jgit.internal.storage.file.PackReverseIndex;
+import org.eclipse.jgit.internal.storage.midx.MultiPackIndex;
+import org.eclipse.jgit.internal.storage.midx.MultiPackIndex.PackOffset;
+import org.eclipse.jgit.internal.storage.midx.MultiPackIndexLoader;
+import org.eclipse.jgit.internal.storage.pack.ObjectToPack;
+import org.eclipse.jgit.internal.storage.pack.PackOutputStream;
+import org.eclipse.jgit.lib.AbbreviatedObjectId;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.BitmapIndex;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectIdSet;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.util.BlockList;
+
+/**
+ * Implementation of a DfsPackfile that tries to solve the queries in a
+ * multipack index before resorting to the real packs.
+ * <p>
+ * It uses the position in the multipack index of the objects as their "offset".
+ */
+public final class DfsPackFileMidx extends DfsPackFile {
+
+	private static final int REF_POSITION = 0;
+
+	private final List<DfsPackFile> packs;
+
+	// The required packs, in the order specified in the multipack index
+	// Initialized lazily, when the midx is loaded
+	private final DfsPackFile[] packsInIdOrder;
+
+	private MultiPackIndex midx;
+
+	private final DfsPackFileMidx base;
+
+	private final VOffsetCalculator offsetCalculator;
+
+	static DfsPackFileMidx create(DfsBlockCache cache, DfsPackDescription desc,
+			List<DfsPackFile> reqPacks, DfsPackFileMidx base) {
+		return new DfsPackFileMidx(cache, desc, reqPacks, base);
+	}
+
+	private DfsPackFileMidx(DfsBlockCache cache, DfsPackDescription desc,
+			List<DfsPackFile> requiredPacks, @Nullable DfsPackFileMidx base) {
+		super(cache, desc);
+		this.base = base;
+		this.packs = requiredPacks;
+		String[] coveredPackNames = desc.getCoveredPacks().stream()
+				.map(DfsPackDescription::getPackName).toArray(String[]::new);
+		packsInIdOrder = getPacksInMidxIdOrder(coveredPackNames);
+		offsetCalculator = VOffsetCalculator.fromPacks(packsInIdOrder,
+				base != null ? base.getOffsetCalculator() : null);
+		this.length = offsetCalculator.getMaxOffset();
+	}
+
+	private MultiPackIndex midx(DfsReader ctx) throws IOException {
+		if (midx != null) {
+			return midx;
+		}
+
+		DfsStreamKey revKey = desc.getStreamKey(MULTI_PACK_INDEX);
+		// Keep the value parsed in the loader, in case the Ref<> is
+		// nullified in ClockBlockCacheTable#reserveSpace
+		// before we read its value.
+		AtomicReference<MultiPackIndex> loadedRef = new AtomicReference<>(null);
+		DfsBlockCache.Ref<MultiPackIndex> cachedRef = cache.getOrLoadRef(revKey,
+				REF_POSITION, () -> {
+					RefWithSize midx1 = loadMultiPackIndex(ctx, desc);
+					loadedRef.set(midx1.idx);
+					return new DfsBlockCache.Ref<>(revKey, REF_POSITION,
+							midx1.size, midx1.idx);
+				});
+		// if (loadedRef.get() == null) {
+		// ctx.stats.ridxCacheHit;
+		// }
+		midx = cachedRef.get() != null ? cachedRef.get() : loadedRef.get();
+		return midx;
+	}
+
+	private static RefWithSize loadMultiPackIndex(DfsReader ctx,
+			DfsPackDescription desc) throws IOException {
+		try (ReadableChannel rc = ctx.db.openFile(desc, MULTI_PACK_INDEX)) {
+			MultiPackIndex midx = MultiPackIndexLoader
+					.read(Channels.newInputStream(rc));
+			// ctx.stats.readIdxBytes += rc.position();
+			return new RefWithSize(midx, midx.getMemorySize());
+		}
+	}
+
+	private record RefWithSize(MultiPackIndex idx, long size) {
+	}
+
+	private DfsPackFile[] getPacksInMidxIdOrder(String[] packNames) {
+		Map<String, DfsPackFile> byName = packs.stream()
+				.collect(Collectors.toUnmodifiableMap(
+						p -> p.getPackDescription().getPackName(),
+						Function.identity()));
+		DfsPackFile[] result = new DfsPackFile[desc.getCoveredPacks().size()];
+		for (int i = 0; i < packNames.length; i++) {
+			DfsPackFile pack = byName.get(packNames[i]);
+			if (pack == null) {
+				// This should have been checked in the object db
+				// when the pack description was loaded
+				throw new IllegalStateException("Required pack missing"); //$NON-NLS-1$
+			}
+			result[i] = pack;
+		}
+		return result;
+	}
+
+	// Visible for testing
+	VOffsetCalculator getOffsetCalculator() {
+		return offsetCalculator;
+	}
+
+	@Override
+	public PackIndex getPackIndex(DfsReader ctx) {
+		throw new IllegalStateException(
+				"Shouldn't use multipack index if the primary index is needed"); //$NON-NLS-1$
+	}
+
+	@Override
+	public ObjectIdSet asObjectIdSet(DfsReader ctx) throws IOException {
+		MultiPackIndex multiPackIndex = midx(ctx);
+		return objectId -> multiPackIndex.hasObject(objectId);
+	}
+
+	@Override
+	public PackReverseIndex getReverseIdx(DfsReader ctx) {
+		throw new IllegalStateException(
+				"Shouldn't use multipack index if the reverse index is needed"); //$NON-NLS-1$
+	}
+
+	@Override
+	public PackBitmapIndex getBitmapIndex(DfsReader ctx) throws IOException {
+		// TODO(ifrade): at some point we will have bitmaps over the multipack
+		// index
+		// At the moment bitmap is in GC, at the end of the chain
+		if (base != null) {
+			return base.getBitmapIndex(ctx);
+		}
+
+		for (DfsPackFile pack : packsInIdOrder) {
+			PackBitmapIndex bitmapIndex = pack.getBitmapIndex(ctx);
+			if (bitmapIndex != null) {
+				return bitmapIndex;
+			}
+		}
+		return null;
+	}
+
+	@Override
+	List<DfsPackFile> fullyIncludedIn(DfsReader ctx,
+			BitmapIndex.BitmapBuilder need) throws IOException {
+		List<DfsPackFile> fullyIncluded = new ArrayList<>();
+		for (DfsPackFile pack : packs) {
+			List<DfsPackFile> includedPacks = pack.fullyIncludedIn(ctx, need);
+			if (!includedPacks.isEmpty()) {
+				fullyIncluded.addAll(includedPacks);
+			}
+		}
+
+		if (base != null) {
+			fullyIncluded.addAll(base.fullyIncludedIn(ctx, need));
+		}
+
+		return fullyIncluded;
+	}
+
+	@Override
+	public CommitGraph getCommitGraph(DfsReader ctx) throws IOException {
+		for (DfsPackFile pack : packs) {
+			CommitGraph cg = pack.getCommitGraph(ctx);
+			if (cg != null) {
+				return cg;
+			}
+		}
+		return null;
+	}
+
+	/**
+	 * Count of objects in this <b>pack</> (i.e. including, recursively, its
+	 * base)
+	 *
+	 * @param ctx
+	 *            a reader
+	 * @return count of objects in this pack, including its bases
+	 * @throws IOException
+	 *             an error reading a midx in the chain
+	 */
+	private int getObjectCount(DfsReader ctx) throws IOException {
+		int baseObjectCount = base == null ? 0 : base.getObjectCount(ctx);
+		return midx(ctx).getObjectCount() + baseObjectCount;
+	}
+
+	/**
+	 * Packs indexed by this multipack index (base NOT included)
+	 *
+	 * @return packs indexed by this multipack index
+	 */
+	public List<DfsPackFile> getCoveredPacks() {
+		return packs;
+	}
+
+	/**
+	 * All packs indexed by this multipack index and its chain
+	 * <p>
+	 * This does not include the inner multipack indexes themselves, only their
+	 * covered packs.
+	 *
+	 * @return packs indexed by this multipack index and its parents.
+	 */
+	public List<DfsPackFile> getAllCoveredPacks() {
+		List<DfsPackFile> coveredPacks = new ArrayList<>(packs);
+		DfsPackFileMidx base = getMultipackIndexBase();
+		while (base != null) {
+			coveredPacks.addAll(base.getCoveredPacks());
+			base = base.getMultipackIndexBase();
+		}
+
+		return coveredPacks;
+	}
+
+	/**
+	 * Base of this multipack index
+	 * <p>
+	 * If this midx is part of a chain, this is its parent
+	 *
+	 * @return the base of this multipack index
+	 */
+	public DfsPackFileMidx getMultipackIndexBase() {
+		return base;
+	}
+
+	@Override
+	public int findIdxPosition(DfsReader ctx, AnyObjectId id)
+			throws IOException {
+		int p = midx(ctx).findPosition(id);
+		if (p >= 0) {
+			int baseObjects = base == null ? 0 : base.getObjectCount(ctx);
+			return p + baseObjects;
+		}
+
+		if (base == null) {
+			return -1;
+		}
+
+		return base.findIdxPosition(ctx, id);
+	}
+
+	@Override
+	public boolean hasObject(DfsReader ctx, AnyObjectId id) throws IOException {
+		if (midx(ctx).hasObject(id)) {
+			return true;
+		}
+
+		if (base == null) {
+			return false;
+		}
+
+		return base.hasObject(ctx, id);
+	}
+
+	@Override
+	ObjectLoader get(DfsReader ctx, AnyObjectId id) throws IOException {
+		PackOffset location = midx(ctx).find(id);
+		if (location != null) {
+			return packsInIdOrder[location.getPackId()].get(ctx, id);
+		}
+
+		if (base == null) {
+			return null;
+		}
+
+		return base.get(ctx, id);
+	}
+
+	@Override
+	ObjectLoader load(DfsReader ctx, long midxOffset) throws IOException {
+		DfsPackOffset location = offsetCalculator.decode(midxOffset);
+		if (location == null) {
+			return null;
+		}
+		return location.getPack().load(ctx, location.getPackOffset());
+	}
+
+	@Override
+	long findOffset(DfsReader ctx, AnyObjectId id) throws IOException {
+		PackOffset location = midx(ctx).find(id);
+		if (location != null) {
+			return offsetCalculator.encode(location);
+		}
+
+		if (base == null) {
+			return -1;
+		}
+
+		return base.findOffset(ctx, id);
+	}
+
+	@Override
+	void resolve(DfsReader ctx, Set<ObjectId> matches, AbbreviatedObjectId id,
+			int matchLimit) throws IOException {
+		midx(ctx).resolve(matches, id, matchLimit);
+		if (matches.size() < matchLimit && base != null) {
+			base.resolve(ctx, matches, id, matchLimit);
+		}
+	}
+
+	@Override
+	void copyPackAsIs(PackOutputStream out, DfsReader ctx) throws IOException {
+		// Assumming the order of the packs does not really matter
+		for (DfsPackFile pack : packs) {
+			pack.copyPackAsIs(out, ctx);
+		}
+
+		if (base != null) {
+			base.copyPackAsIs(out, ctx);
+		}
+	}
+
+	@Override
+	void copyAsIs(PackOutputStream out, DfsObjectToPack src, boolean validate,
+			DfsReader ctx) throws IOException,
+			StoredObjectRepresentationNotAvailableException {
+		if (src.pack != this) {
+			throw new IllegalArgumentException(
+					"pack mismatch in object description"); //$NON-NLS-1$
+		}
+
+		DfsPackOffset location = offsetCalculator.decode(src.offset);
+		// The real pack requires the real offset
+		src.offset = location.getPackOffset();
+		location.getPack().copyAsIs(out, src, validate, ctx);
+		// Restore, just in case
+		src.offset = location.getPackStart() + location.getPackOffset();
+	}
+
+	@Override
+	byte[] getDeltaHeader(DfsReader ctx, long pos)
+			throws IOException, DataFormatException {
+		DfsPackOffset location = offsetCalculator.decode(pos);
+		return location.getPack().getDeltaHeader(ctx, location.getPackOffset());
+	}
+
+	@Override
+	int getObjectType(DfsReader ctx, long pos) throws IOException {
+		DfsPackOffset location = offsetCalculator.decode(pos);
+		return location.getPack().getObjectType(ctx, location.getPackOffset());
+	}
+
+	@Override
+	long getObjectSize(DfsReader ctx, AnyObjectId id) throws IOException {
+		PackOffset local = midx(ctx).find(id);
+		if (local != null) {
+			return packsInIdOrder[local.getPackId()].getObjectSize(ctx, id);
+		}
+
+		if (base == null) {
+			return -1;
+		}
+
+		return base.getObjectSize(ctx, id);
+	}
+
+	@Override
+	long getObjectSize(DfsReader ctx, long pos) throws IOException {
+		if (pos < 0) {
+			return -1;
+		}
+		DfsPackOffset location = offsetCalculator.decode(pos);
+		return location.getPack().getObjectSize(ctx, location.getPackOffset());
+	}
+
+	@Override
+	boolean hasObjectSizeIndex(DfsReader ctx) {
+		return false;
+	}
+
+	@Override
+	int getObjectSizeIndexThreshold(DfsReader ctx) {
+		return Integer.MAX_VALUE;
+	}
+
+	@Override
+	long getIndexedObjectSize(DfsReader ctx, int idxPosition) {
+		// TODO(ifrade): if we forward to the pack, it reads its primary index
+		return -1;
+	}
+
+	@Override
+	List<DfsObjectToPack> findAllFromPack(DfsReader ctx,
+			Iterable<ObjectToPack> objects, boolean skipFound)
+			throws IOException {
+		List<DfsObjectToPack> tmp = new BlockList<>();
+		List<ObjectToPack> notFoundHere = new BlockList<>();
+		for (ObjectToPack obj : objects) {
+			DfsObjectToPack otp = (DfsObjectToPack) obj;
+			if (skipFound && otp.isFound()) {
+				continue;
+			}
+			long p = offsetCalculator.encode(midx(ctx).find(otp));
+			if (p < 0) {
+				notFoundHere.add(otp);
+				continue;
+			}
+			otp.setOffset(p);
+			tmp.add(otp);
+		}
+
+		if (base != null && !notFoundHere.isEmpty()) {
+			List<DfsObjectToPack> inChain = base.findAllFromPack(ctx,
+					notFoundHere, skipFound);
+			tmp.addAll(inChain);
+		}
+		tmp.sort(OFFSET_SORT);
+		return tmp;
+	}
+
+	@Override
+	void fillRepresentation(DfsObjectRepresentation r, long offset,
+			DfsReader ctx) throws IOException {
+		DfsPackOffset location = offsetCalculator.decode(offset);
+		if (location == null) {
+			throw new IllegalArgumentException("Invalid offset in midx"); //$NON-NLS-1$
+		}
+		// This will load the reverse index. The multipack index removes
+		// duplicated objects, so next offset in midx is not necessarily the
+		// following object in pack.
+		location.getPack().fillRepresentation(r, location.getPackOffset(), ctx);
+		// We return *our* midx offset, not the one in the pack
+		r.offset = offset;
+	}
+
+	@Override
+	void fillRepresentation(DfsObjectRepresentation r, long offset,
+			DfsReader ctx, PackReverseIndex rev) {
+		// This method shouldn't be called on the midx pack
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	boolean isCorrupt(long offset) {
+		// The index must have been loaded before to have this offset
+		DfsPackOffset location = offsetCalculator.decode(offset);
+		if (location == null) {
+			throw new IllegalArgumentException("Invalid offset in midx"); //$NON-NLS-1$
+		}
+		return location.getPack().isCorrupt(location.getPackOffset());
+	}
+
+	@Override
+	DfsBlock readOneBlock(long pos, DfsReader ctx, ReadableChannel rc)
+			throws IOException {
+		// The index must have been loaded before to have this offset
+		DfsPackOffset location = offsetCalculator.decode(pos);
+		return new DfsBlockMidx(location.getPack().readOneBlock(
+				location.getPackOffset(), ctx, rc), location.getPackStart());
+	}
+
+	@Override
+	DfsBlock getOrLoadBlock(long pos, DfsReader ctx) throws IOException {
+		// The index must have been loaded before to have this offset
+		DfsPackOffset location = offsetCalculator.decode(pos);
+		return new DfsBlockMidx(location.getPack().getOrLoadBlock(
+				location.getPackOffset(), ctx), location.getPackStart());
+	}
+
+	// Visible for testing
+	static class VOffsetCalculator {
+		private final DfsPackFile[] packs;
+
+		private final long[] accSizes;
+
+		private final long baseMaxOffset;
+
+		private final VOffsetCalculator baseOffsetCalculator;
+
+		private final DfsPackOffset poBuffer = new DfsPackOffset();
+
+		static VOffsetCalculator fromPacks(DfsPackFile[] packsInIdOrder,
+				VOffsetCalculator baseOffsetCalculator) {
+			long[] accSizes = new long[packsInIdOrder.length + 1];
+			accSizes[0] = 0;
+			for (int i = 0; i < packsInIdOrder.length; i++) {
+				accSizes[i + 1] = accSizes[i] + packsInIdOrder[i]
+						.getPackDescription().getFileSize(PACK);
+			}
+			return new VOffsetCalculator(packsInIdOrder, accSizes,
+					baseOffsetCalculator);
+		}
+
+		VOffsetCalculator(DfsPackFile[] packs, long[] packSizes,
+				VOffsetCalculator baseOffsetCalculator) {
+			this.packs = packs;
+			this.baseOffsetCalculator = baseOffsetCalculator;
+			this.baseMaxOffset = baseOffsetCalculator != null
+					? baseOffsetCalculator.getMaxOffset()
+					: 0;
+			accSizes = packSizes;
+		}
+
+		long encode(PackOffset location) {
+			if (location == null) {
+				return -1;
+			}
+			return location.getOffset() + accSizes[location.getPackId()]
+					+ baseMaxOffset;
+		}
+
+		DfsPackOffset decode(long voffset) {
+			if (voffset == -1) {
+				return null;
+			}
+
+			if (voffset < baseMaxOffset) {
+				return baseOffsetCalculator.decode(voffset);
+			}
+
+			long localOffset = voffset - baseMaxOffset;
+			for (int i = 0; i < accSizes.length; i++) {
+				if (localOffset <= accSizes[i]) {
+					return poBuffer.setValues(packs[i - 1],
+							accSizes[i - 1] + baseMaxOffset, voffset);
+				}
+			}
+			throw new IllegalArgumentException("Asking offset beyond limits"); //$NON-NLS-1$
+		}
+
+		long getMaxOffset() {
+			return accSizes[accSizes.length - 1] + baseMaxOffset;
+		}
+	}
+
+	static class DfsPackOffset {
+		private DfsPackFile pack;
+
+		private long packStart;
+
+		private long offset;
+
+		private DfsPackOffset setValues(DfsPackFile pack, long packStart,
+				long globalOffset) {
+			this.pack = pack;
+			this.packStart = packStart;
+			this.offset = globalOffset;
+			return this;
+		}
+
+		/**
+		 * The pack
+		 *
+		 * @return the pack
+		 */
+		DfsPackFile getPack() {
+			return pack;
+		}
+
+		/**
+		 * Where the pack starts in the total midx concat of packs
+		 * <p>
+		 * After loading a block, callers will use "midx-offsets" to refer to
+		 * positions. We need this to bring the value to the block.
+		 *
+		 * @return offset where the pack starts, when all packs are concatenated
+		 *         in midx order.
+		 */
+		long getPackStart() {
+			return packStart;
+		}
+
+		/**
+		 * Offset inside the pack (regular offset)
+		 *
+		 * @return offset inside this pack
+		 */
+		long getPackOffset() {
+			return offset - packStart;
+		}
+	}
+}
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 f50cd59..2272cc5 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
@@ -24,6 +24,7 @@
 import java.util.List;
 import java.util.Optional;
 import java.util.Set;
+import java.util.stream.Collectors;
 import java.util.zip.DataFormatException;
 import java.util.zip.Inflater;
 
@@ -149,10 +150,12 @@ public Optional<CommitGraph> getCommitGraph() throws IOException {
 	public Collection<CachedPack> getCachedPacksAndUpdate(
 		BitmapBuilder needBitmap) throws IOException {
 		for (DfsPackFile pack : db.getPacks()) {
-			PackBitmapIndex bitmapIndex = pack.getBitmapIndex(this);
-			if (needBitmap.removeAllOrNone(bitmapIndex))
-				return Collections.<CachedPack> singletonList(
-						new DfsCachedPack(pack));
+			// p != pack in the multipack index case
+			List<DfsPackFile> p = pack.fullyIncludedIn(this, needBitmap);
+			if (!p.isEmpty()) {
+				return p.stream().map(DfsCachedPack::new)
+						.collect(Collectors.toUnmodifiableList());
+			}
 		}
 		return Collections.emptyList();
 	}
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 fe8dc17..ddf6f39 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
@@ -133,7 +133,8 @@ public void setReadableChannelBlockSizeForTest(int blockSize) {
 
 		@Override
 		protected synchronized List<DfsPackDescription> listPacks() {
-			return packs;
+			return useMultipackIndex() ? MidxPackFilter.useMidx(packs)
+					: MidxPackFilter.skipMidxs(packs);
 		}
 
 		@Override
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/MidxPackFilter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/MidxPackFilter.java
new file mode 100644
index 0000000..50de974
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/MidxPackFilter.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2025, 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.dfs;
+
+import org.eclipse.jgit.internal.storage.pack.PackExt;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Format a flat list of packs and midxs into a valid list of packs.
+ * <p>
+ * A valid list of packs is either:
+ * <ul>
+ * <li>A list with midxs and uncovered packs</li>
+ * <li>A list of packs (without any midx)</li>
+ * </ul>
+ */
+public class MidxPackFilter {
+
+	private MidxPackFilter() {
+	}
+
+	/**
+	 * Reorganize the flat list of packs removing the midxs
+	 *
+	 * @param packs
+	 *            flat list of all packs in the repo, may include midx. Packs
+	 *            covered by the midx appear also on their own.
+	 * @return a list of packs without midxs
+	 */
+	public static List<DfsPackDescription> skipMidxs(
+			List<DfsPackDescription> packs) {
+		// Covered packs appear also on their own in the list, so we can just
+		// take the midx out.
+		return packs.stream()
+				.filter(desc -> !desc.hasFileExt(PackExt.MULTI_PACK_INDEX))
+				.collect(Collectors.toList());
+	}
+
+	/**
+	 * Remove from the list any packs covered by midxs.
+	 * <p>
+	 * This verifies that all referenced packs by the midxs exist.
+	 *
+	 * @param packs
+	 *            list of packs with maybe some midxs
+	 * @return midxs and uncovered packs. All the input packs if no midx. Ignore
+	 *         midxs with missing covered packs.
+	 */
+	public static List<DfsPackDescription> useMidx(
+			List<DfsPackDescription> packs) {
+		// Take the packs covered by the midxs out of the list
+		List<DfsPackDescription> midxs = packs.stream()
+				.filter(desc -> desc.hasFileExt(PackExt.MULTI_PACK_INDEX))
+				.toList();
+		if (midxs.isEmpty()) {
+			return packs;
+		}
+
+		Set<DfsPackDescription> inputPacks = new HashSet<>(packs);
+		Set<DfsPackDescription> allCoveredPacks = new HashSet<>();
+		for (DfsPackDescription midx : midxs) {
+			Set<DfsPackDescription> coveredPacks = new HashSet<>();
+			findCoveredPacks(midx, coveredPacks);
+			if (!inputPacks.containsAll(coveredPacks)) {
+				// This midx references packs not in the pack db.
+				// It could be part of a chain, so we just ignore all midxs
+				return skipMidxs(packs);
+			}
+			allCoveredPacks.addAll(coveredPacks);
+		}
+
+		return packs.stream().filter(d -> !allCoveredPacks.contains(d))
+				.collect(Collectors.toList());
+	}
+
+	private static void findCoveredPacks(DfsPackDescription midx,
+			Set<DfsPackDescription> covered) {
+		if (!midx.getCoveredPacks().isEmpty()) {
+			covered.addAll(midx.getCoveredPacks());
+		}
+
+		if (midx.getMultiPackIndexBase() != null) {
+			findCoveredPacks(midx.getMultiPackIndexBase(), covered);
+			covered.add(midx.getMultiPackIndexBase());
+		}
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/MidxPackList.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/MidxPackList.java
new file mode 100644
index 0000000..d26b157
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/MidxPackList.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2025, 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.dfs;
+
+import static org.eclipse.jgit.internal.storage.pack.PackExt.MULTI_PACK_INDEX;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Queue;
+import java.util.Set;
+import java.util.TreeSet;
+
+/**
+ * Helper class to manipulate a list of packs with (maybe) midxs.
+ **/
+public final class MidxPackList {
+
+	private static final Comparator<DfsPackFile> PACK_NAME_COMPARATOR = Comparator
+			.comparing(pack -> pack.getPackDescription().getPackName());
+
+	/**
+	 * Wrap the packs list into a MidxPackList
+	 * <p>
+	 * The input list is well-formed, doesn't have duplicated packs/midxs.
+	 *
+	 * @param packs
+	 *            list of packs (regular or tip midxs)
+	 * @return a MidxPackList instance
+	 */
+	public static MidxPackList create(DfsPackFile[] packs) {
+		return new MidxPackList(packs);
+	}
+
+	private final List<DfsPackFile> packs;
+
+	private MidxPackList(DfsPackFile[] packs) {
+		this.packs = Arrays.asList(packs);
+	}
+
+	/**
+	 * Get all plain packs in the list, either top-level or inside midxs
+	 *
+	 * @return a list of all "real" packs in this pack list, either top level or
+	 *         inside midxs.
+	 **/
+	public List<DfsPackFile> getAllPlainPacks() {
+		List<DfsPackFile> plainPacks = new ArrayList<>();
+		Queue<DfsPackFile> pending = new ArrayDeque<>(packs);
+		while (!pending.isEmpty()) {
+			DfsPackFile pack = pending.poll();
+			if (pack instanceof DfsPackFileMidx midxPack) {
+				plainPacks.addAll(midxPack.getCoveredPacks());
+				if (midxPack.getMultipackIndexBase() != null) {
+					pending.add(midxPack.getMultipackIndexBase());
+				}
+			} else {
+				plainPacks.add(pack);
+			}
+		}
+		return plainPacks;
+	}
+
+	/**
+	 * Get all midx in the list, either top-level or inside other midxs
+	 *
+	 * @return a list of all midxs in thist list, either top level or nested
+	 *         inside other midxs.
+	 **/
+	public List<DfsPackFileMidx> getAllMidxPacks() {
+		List<DfsPackFileMidx> topLevelMidxs = packs.stream().filter(
+				p -> p.getPackDescription().hasFileExt(MULTI_PACK_INDEX))
+				.map(p -> (DfsPackFileMidx) p).toList();
+
+		List<DfsPackFileMidx> midxPacks = new ArrayList<>();
+		Queue<DfsPackFileMidx> pending = new ArrayDeque<>(topLevelMidxs);
+		while (!pending.isEmpty()) {
+			DfsPackFileMidx midx = pending.poll();
+			midxPacks.add(midx);
+			if (midx.getMultipackIndexBase() != null) {
+				pending.add(midx.getMultipackIndexBase());
+			}
+		}
+		return midxPacks;
+	}
+
+	/**
+	 * Alias for {@link #findAllCoveringMidxs(List)}, as convenience for tests
+	 *
+	 * @param pack
+	 *            a single pack
+	 * @return all the midxs that include (directly or indirectly) the pack
+	 */
+	Set<DfsPackFileMidx> findAllCoveringMidxs(DfsPackFile pack) {
+		return findAllCoveringMidxs(List.of(pack));
+	}
+
+	/**
+	 * Return all the midxs that cover (directoy or indirectly) a set of packs
+	 * <p>
+	 * In a chain like midx3(p6, p5) -> midx2(p4, p3) -> midx1(p2, p1), if we
+	 * check for p3, midx3 and midx2 are covering it and midx1 is not.
+	 *
+	 * @param queryPacks
+	 *            subset of packs we are checking for coverage
+	 * @return set of midxs from the packlist that should also be removed
+	 */
+	public Set<DfsPackFileMidx> findAllCoveringMidxs(
+			List<DfsPackFile> queryPacks) {
+		if (queryPacks.isEmpty()) {
+			return Collections.emptySet();
+		}
+
+		List<DfsPackFileMidx> topLevelMidxs = packs.stream().filter(
+				p -> p.getPackDescription().hasFileExt(MULTI_PACK_INDEX))
+				.map(p -> (DfsPackFileMidx) p).toList();
+
+		if (topLevelMidxs.isEmpty()) {
+			return Collections.emptySet();
+		}
+
+		// TODO(ifrade): Delete from queryPacks as we find them and stop if we
+		// are done
+		Set<DfsPackFileMidx> impactedMidxs = asSet(List.of());
+		for (DfsPackFileMidx midx : topLevelMidxs) {
+			List<DfsPackFileMidx> visitedMidxs = new ArrayList<>();
+			DfsPackFileMidx current = midx;
+			while (current != null) {
+				visitedMidxs.add(current);
+				if (containsAny(current.getCoveredPacks(), queryPacks)) {
+					// Anything above in the chain is also covering this pack
+					impactedMidxs.addAll(visitedMidxs);
+					// Reset the list and keep going, maybe something
+					// deeper in the chain is also affected
+					visitedMidxs.clear();
+				}
+				current = current.getMultipackIndexBase();
+			}
+			visitedMidxs.clear();
+		}
+
+		return impactedMidxs;
+	}
+
+	private static boolean containsAny(List<DfsPackFile> inMidx,
+			List<DfsPackFile> queryPacks) {
+		Set<DfsPackFile> inMidxSet = asSet(inMidx);
+		return queryPacks.stream().anyMatch(inMidxSet::contains);
+	}
+
+	private static <T extends DfsPackFile> TreeSet<T> asSet(
+			List<T> initialValues) {
+		TreeSet<T> theSet = new TreeSet<>(PACK_NAME_COMPARATOR);
+		theSet.addAll(initialValues);
+		return theSet;
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableDatabase.java
index 64f8c9b..c46a17b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableDatabase.java
@@ -622,9 +622,7 @@ private static void writeConvertTable(Repository repo, ReftableWriter w,
 		if (writeLogs) {
 			for (Ref r : refs) {
 				ReflogReader rlr = refDb.getReflogReader(r);
-				if (rlr != null) {
-					size = Math.max(rlr.getReverseEntries().size(), size);
-				}
+				size = Math.max(rlr.getReverseEntries().size(), size);
 			}
 		}
 		// We must use 1 here, nextUpdateIndex() on the empty stack is 1.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackObjectSizeIndexHelper.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackObjectSizeIndexHelper.java
index acef741..07a9495 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackObjectSizeIndexHelper.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackObjectSizeIndexHelper.java
@@ -46,7 +46,7 @@ public static void forAllPacks(ObjectDirectory db, ProgressMonitor pm)
 			List<PackedObjectInfo> objectsInPack = getObjectsInPack(wc, pack,
 					pm);
 			LOG.debug(String.format("    index has %d objects", //$NON-NLS-1$
-					objectsInPack.size()));
+					Integer.valueOf(objectsInPack.size())));
 			if (objectsInPack.isEmpty()) {
 				continue;
 			}
@@ -61,7 +61,7 @@ public static void forAllPacks(ObjectDirectory db, ProgressMonitor pm)
 				writer.write(objectsInPack);
 			}
 			LOG.info(String.format("     done writing. Took %d ms", //$NON-NLS-1$
-					System.currentTimeMillis() - start));
+					Long.valueOf(System.currentTimeMillis() - start)));
 		}
 	}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/MultiPackIndexLoader.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/MultiPackIndexLoader.java
index 61caddc..1d4f47a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/MultiPackIndexLoader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/MultiPackIndexLoader.java
@@ -112,7 +112,8 @@ public static MultiPackIndex read(InputStream fd)
 		int v = hdr[4];
 		if (v != 1) {
 			throw new MultiPackIndexFormatException(MessageFormat
-					.format(JGitText.get().unsupportedMIDXVersion, v));
+					.format(JGitText.get().unsupportedMIDXVersion,
+							Integer.valueOf(v)));
 		}
 
 		// Read the object Id version (1 byte)
@@ -325,7 +326,8 @@ private static void assertPackCounts(int headerCount,
 			if (headerCount != packfileNamesCount) {
 				throw new MultiPackIndexFormatException(MessageFormat.format(
 						JGitText.get().multiPackIndexPackCountMismatch,
-						headerCount, packfileNamesCount));
+						Integer.valueOf(headerCount),
+						Integer.valueOf(packfileNamesCount)));
 			}
 		}
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/MultiPackIndexV1.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/MultiPackIndexV1.java
index e80958f..8ae1fe6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/MultiPackIndexV1.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/MultiPackIndexV1.java
@@ -256,36 +256,44 @@ void resolve(Set<ObjectId> matches, AbbreviatedObjectId id,
 				return;
 			}
 
-			int high = fanoutTable[id.getFirstByte()];
-			int low = id.getFirstByte() == 0 ? 0
-					: fanoutTable[id.getFirstByte() - 1];
-			do {
-				int p = (low + high) >>> 1;
+			int levelOne = id.getFirstByte();
+			int high = fanoutTable[levelOne];
+			int low = 0;
+			if (levelOne > 0) {
+				low = fanoutTable[levelOne - 1];
+			}
+
+			int p = -1;
+			boolean found = false;
+			while (low < high) {
+				p = (low + high) >>> 1;
 				int cmp = id.prefixCompare(oidLookup, idOffset(p));
 				if (cmp < 0) {
 					high = p;
-					continue;
-				}
-
-				if (cmp > 0) {
+				} else if (cmp == 0) {
+					found = true;
+					break;
+				} else {
 					low = p + 1;
-					continue;
 				}
+			}
 
-				// Got a match.
-				// We may have landed in the middle of the matches. Move
-				// backwards to the start of matches, then walk forwards.
-				while (0 < p
-						&& id.prefixCompare(oidLookup, idOffset(p - 1)) == 0) {
-					p--;
-				}
-				while (p < high && id.prefixCompare(oidLookup, idOffset(p)) == 0
-						&& matches.size() < matchLimit) {
-					matches.add(ObjectId.fromRaw(oidLookup, idOffset(p)));
-					p++;
-				}
+			if (!found) {
 				return;
-			} while (low < high);
+			}
+
+			// Got a match.
+			// We may have landed in the middle of the matches. Move
+			// backwards to the start of matches, then walk forwards.
+			while (0 < p && id.prefixCompare(oidLookup, idOffset(p - 1)) == 0) {
+				p--;
+			}
+
+			while (p < high && id.prefixCompare(oidLookup, idOffset(p)) == 0
+					&& matches.size() < matchLimit) {
+				matches.add(ObjectId.fromRaw(oidLookup, idOffset(p)));
+				p++;
+			}
 		}
 
 		private int idOffset(int position) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/PackIndexMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/PackIndexMerger.java
index f236658..fd040b9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/PackIndexMerger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/PackIndexMerger.java
@@ -14,7 +14,6 @@
 import java.util.List;
 import java.util.Map;
 import java.util.NoSuchElementException;
-import java.util.stream.Collectors;
 
 import org.eclipse.jgit.internal.storage.file.PackIndex;
 import org.eclipse.jgit.lib.AnyObjectId;
@@ -93,12 +92,17 @@ private void fill(int packId, PackIndex.MutableEntry other) {
 
 	private final int uniqueObjectCount;
 
+	/**
+	 * Build a common view of these pack indexes
+	 * <p>
+	 * Order matters: in case of duplicates, the first pack with the object wins
+	 *
+	 * @param packs
+	 *            map of pack names to indexes, ordered.
+	 */
 	PackIndexMerger(Map<String, PackIndex> packs) {
-		this.packNames = packs.keySet().stream().sorted()
-				.collect(Collectors.toUnmodifiableList());
-
-		this.indexes = packNames.stream().map(packs::get)
-				.collect(Collectors.toUnmodifiableList());
+		this.packNames = packs.keySet().stream().toList();
+		this.indexes = packs.values().stream().toList();
 
 		// Iterate for duplicates
 		int objectCount = 0;
@@ -252,7 +256,7 @@ public MidxMutableEntry next() {
 			mutableEntry.fill(winner.getPackId(), winner.peek());
 			if (winner.next() == null) {
 				indexIterators.remove(winner);
-			};
+			}
 			return mutableEntry;
 		}
 	}
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 c455032..56b801e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
@@ -1115,4 +1115,11 @@ public final class ConfigConstants {
 	 * @since 7.2
 	 */
 	public static final String CONFIG_KEY_AUTOREFRESH = "autorefresh";
+
+	/**
+	 * The "multiPackIndex" key
+	 *
+	 * @since 7.5
+	 */
+	public static final String CONFIG_KEY_MULTIPACKINDEX = "multiPackIndex";
 }
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 0e27b27..1c6be07 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java
@@ -128,6 +128,13 @@ public enum LogRefUpdates {
 	public static final boolean DEFAULT_COMMIT_GRAPH_ENABLE = false;
 
 	/**
+	 * Default value of multipack index enable option: {@value}
+	 *
+	 * @since 7.5
+	 */
+	public static final boolean DEFAULT_MULTIPACK_INDEX_ENABLE = false;
+
+	/**
 	 * Permissible values for {@code core.trustPackedRefsStat}.
 	 *
 	 * @since 6.1.1
@@ -205,6 +212,8 @@ public enum TrustStat {
 
 	private final boolean commitGraph;
 
+	private final boolean multiPackIndex;
+
 	private final TrustStat trustStat;
 
 	private final TrustStat trustPackedRefsStat;
@@ -264,6 +273,9 @@ public enum HideDotFiles {
 		commitGraph = rc.getBoolean(ConfigConstants.CONFIG_CORE_SECTION,
 				ConfigConstants.CONFIG_COMMIT_GRAPH,
 				DEFAULT_COMMIT_GRAPH_ENABLE);
+		multiPackIndex = rc.getBoolean(ConfigConstants.CONFIG_CORE_SECTION,
+				ConfigConstants.CONFIG_KEY_MULTIPACKINDEX,
+				DEFAULT_MULTIPACK_INDEX_ENABLE);
 
 		trustStat = parseTrustStat(rc);
 		trustPackedRefsStat = parseTrustPackedRefsStat(rc);
@@ -378,6 +390,19 @@ public boolean enableCommitGraph() {
 	}
 
 	/**
+	 * Whether to read the multipack index file (if it exists). Default to
+	 * {@value org.eclipse.jgit.lib.CoreConfig#DEFAULT_MULTIPACK_INDEX_ENABLE}.
+	 *
+	 * @return whether to use the multipack index in the repo. If false, just
+	 *         ignore the multipack index.
+	 *
+	 * @since 7.5
+	 */
+	public boolean useMultiPackIndex() {
+		return multiPackIndex;
+	}
+
+	/**
 	 * Get how far we can trust file attributes of packed-refs file which is
 	 * used to store {@link org.eclipse.jgit.lib.Ref}s in
 	 * {@link org.eclipse.jgit.internal.storage.file.RefDirectory}.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java
index 3059f28..13d0fc2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java
@@ -32,7 +32,7 @@
  */
 public class DefaultTypedConfigGetter implements TypedConfigGetter {
 
-	@SuppressWarnings("boxed")
+	@SuppressWarnings({ "boxed", "boxing" })
 	@Override
 	public boolean getBoolean(Config config, String section, String subsection,
 			String name, boolean defaultValue) {
@@ -115,6 +115,7 @@ public <T extends Enum<?>> T getEnum(Config config, T[] all, String section,
 				JGitText.get().enumValueNotSupported2, section, name, value));
 	}
 
+	@SuppressWarnings("boxing")
 	@Override
 	public int getInt(Config config, String section, String subsection,
 			String name, int defaultValue) {
@@ -144,6 +145,7 @@ public Integer getInt(Config config, String section, String subsection,
 				.format(JGitText.get().integerValueOutOfRange, section, name));
 	}
 
+	@SuppressWarnings("boxing")
 	@Override
 	public int getIntInRange(Config config, String section, String subsection,
 			String name, int minValue, int maxValue, int defaultValue) {
@@ -173,6 +175,7 @@ public Integer getIntInRange(Config config, String section,
 				subsection, name, val, minValue, maxValue));
 	}
 
+	@SuppressWarnings("boxing")
 	@Override
 	public long getLong(Config config, String section, String subsection,
 			String name, long defaultValue) {
@@ -201,6 +204,7 @@ public Long getLong(Config config, String section, String subsection,
 		}
 	}
 
+	@SuppressWarnings("boxing")
 	@Override
 	public long getTimeUnit(Config config, String section, String subsection,
 			String name, long defaultValue, TimeUnit wantUnit) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BoundaryGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BoundaryGenerator.java
index bdf63ff..ed78047 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BoundaryGenerator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BoundaryGenerator.java
@@ -16,6 +16,8 @@
 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
 import org.eclipse.jgit.errors.MissingObjectException;
 
+import static org.eclipse.jgit.revwalk.RevFlag.UNSHALLOW;
+
 class BoundaryGenerator extends Generator {
 	static final int UNINTERESTING = RevWalk.UNINTERESTING;
 
@@ -82,7 +84,7 @@ RevCommit next() throws MissingObjectException,
 						break;
 					}
 					RevCommit p = c.getParent(i);
-					if ((p.flags & UNINTERESTING) != 0) {
+					if ((p.flags & UNINTERESTING) != 0 && !p.has(UNSHALLOW)) {
 						held.add(p);
 					}
 				}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthGenerator.java
index 664f8fa..3fabbc2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthGenerator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthGenerator.java
@@ -17,6 +17,8 @@
 import org.eclipse.jgit.errors.MissingObjectException;
 import org.eclipse.jgit.lib.ObjectId;
 
+import static org.eclipse.jgit.revwalk.RevFlag.UNSHALLOW;
+
 /**
  * Only produce commits which are below a specified depth.
  *
@@ -32,14 +34,6 @@ class DepthGenerator extends Generator {
 	private final RevWalk walk;
 
 	/**
-	 * Commits which used to be shallow in the client, but which are
-	 * being extended as part of this fetch.  These commits should be
-	 * returned to the caller as UNINTERESTING so that their blobs/trees
-	 * can be marked appropriately in the pack writer.
-	 */
-	private final RevFlag UNSHALLOW;
-
-	/**
 	 * Commits which the normal framework has marked as UNINTERESTING,
 	 * but which we now care about again.  This happens if a client is
 	 * extending a shallow checkout to become deeper--the new commits at
@@ -73,7 +67,6 @@ class DepthGenerator extends Generator {
 
 		this.depth = w.getDepth();
 		this.deepenSince = w.getDeepenSince();
-		this.UNSHALLOW = w.getUnshallowFlag();
 		this.REINTERESTING = w.getReinterestingFlag();
 		this.DEEPEN_NOT = w.getDeepenNotFlag();
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthWalk.java
index a7ffd34..7795ce2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthWalk.java
@@ -64,7 +64,9 @@ default List<ObjectId> getDeepenNots() {
 	 * Get flag marking commits that should become unshallow.
 	 *
 	 * @return flag marking commits that should become unshallow.
+	 * @deprecated use {@link RevFlag#UNSHALLOW} instead
 	 */
+	@Deprecated(forRemoval = true, since = "7.5")
 	RevFlag getUnshallowFlag();
 
 	/**
@@ -136,8 +138,6 @@ public class RevWalk extends org.eclipse.jgit.revwalk.RevWalk implements DepthWa
 
 		private List<ObjectId> deepenNots;
 
-		private final RevFlag UNSHALLOW;
-
 		private final RevFlag REINTERESTING;
 
 		private final RevFlag DEEPEN_NOT;
@@ -155,7 +155,6 @@ public RevWalk(Repository repo, int depth) {
 
 			this.depth = depth;
 			this.deepenNots = Collections.emptyList();
-			this.UNSHALLOW = newFlag("UNSHALLOW"); //$NON-NLS-1$
 			this.REINTERESTING = newFlag("REINTERESTING"); //$NON-NLS-1$
 			this.DEEPEN_NOT = newFlag("DEEPEN_NOT"); //$NON-NLS-1$
 		}
@@ -173,7 +172,6 @@ public RevWalk(ObjectReader or, int depth) {
 
 			this.depth = depth;
 			this.deepenNots = Collections.emptyList();
-			this.UNSHALLOW = newFlag("UNSHALLOW"); //$NON-NLS-1$
 			this.REINTERESTING = newFlag("REINTERESTING"); //$NON-NLS-1$
 			this.DEEPEN_NOT = newFlag("DEEPEN_NOT"); //$NON-NLS-1$
 		}
@@ -240,9 +238,13 @@ public void setDeepenNots(List<ObjectId> deepenNots) {
 			this.deepenNots = Objects.requireNonNull(deepenNots);
 		}
 
+		/**
+		 * @deprecated use {@link RevFlag#UNSHALLOW} instead
+		 */
+		@Deprecated(forRemoval = true, since = "7.5")
 		@Override
 		public RevFlag getUnshallowFlag() {
-			return UNSHALLOW;
+			return RevFlag.UNSHALLOW;
 		}
 
 		@Override
@@ -279,8 +281,6 @@ public class ObjectWalk extends org.eclipse.jgit.revwalk.ObjectWalk implements D
 
 		private List<ObjectId> deepenNots;
 
-		private final RevFlag UNSHALLOW;
-
 		private final RevFlag REINTERESTING;
 
 		private final RevFlag DEEPEN_NOT;
@@ -298,7 +298,6 @@ public ObjectWalk(Repository repo, int depth) {
 
 			this.depth = depth;
 			this.deepenNots = Collections.emptyList();
-			this.UNSHALLOW = newFlag("UNSHALLOW"); //$NON-NLS-1$
 			this.REINTERESTING = newFlag("REINTERESTING"); //$NON-NLS-1$
 			this.DEEPEN_NOT = newFlag("DEEPEN_NOT"); //$NON-NLS-1$
 		}
@@ -316,7 +315,6 @@ public ObjectWalk(ObjectReader or, int depth) {
 
 			this.depth = depth;
 			this.deepenNots = Collections.emptyList();
-			this.UNSHALLOW = newFlag("UNSHALLOW"); //$NON-NLS-1$
 			this.REINTERESTING = newFlag("REINTERESTING"); //$NON-NLS-1$
 			this.DEEPEN_NOT = newFlag("DEEPEN_NOT"); //$NON-NLS-1$
 		}
@@ -362,8 +360,9 @@ public void markRoot(RevObject o) throws MissingObjectException,
 		 */
 		public void markUnshallow(RevObject c) throws MissingObjectException,
 				IncorrectObjectTypeException, IOException {
-			if (c instanceof RevCommit)
-				c.add(UNSHALLOW);
+			if (c instanceof RevCommit) {
+				c.add(RevFlag.UNSHALLOW);
+			}
 			super.markStart(c);
 		}
 
@@ -387,9 +386,13 @@ public List<ObjectId> getDeepenNots() {
 			return deepenNots;
 		}
 
+		/**
+		 * @deprecated use {@link RevFlag#UNSHALLOW} instead
+		 */
+		@Deprecated(forRemoval = true, since = "7.5")
 		@Override
 		public RevFlag getUnshallowFlag() {
-			return UNSHALLOW;
+			return RevFlag.UNSHALLOW;
 		}
 
 		@Override
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevFlag.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevFlag.java
index 3221bf6..f0083b7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevFlag.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevFlag.java
@@ -46,6 +46,18 @@ public class RevFlag {
 	 */
 	public static final RevFlag SEEN = new StaticRevFlag("SEEN", RevWalk.SEEN); //$NON-NLS-1$
 
+	/**
+	 * Set on RevObject instances when generating a navigation for unshallow request.
+	 * <p>
+	 * Commits which used to be shallow in the client, but which are
+	 * being extended as part of this fetch.  These commits should be
+	 * returned to the caller as UNINTERESTING so that their blobs/trees
+	 * can be marked appropriately in the pack writer.
+	 *
+	 * @since 7.5
+	 */
+	public static final RevFlag UNSHALLOW = new StaticRevFlag("UNSHALLOW", RevWalk.UNSHALLOW); //$NON-NLS-1$
+
 	final RevWalk walker;
 
 	final String name;
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 4b8be70..ba7573a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
@@ -163,9 +163,21 @@ public class RevWalk implements Iterable<RevCommit>, AutoCloseable {
 	static final int TREE_REV_FILTER_APPLIED = 1 << 7;
 
 	/**
+	 * Set on a RevObject marked for being unshallowed.
+	 * <p>
+	 * This flag is used by the RevWalk's generators for keeping track
+	 * that some objects have been marked uninteresting, however, they
+	 * need to allow the navigation to continue for managing the unshallow
+	 * of a shallow clone.
+	 *
+	 * @see DepthGenerator
+	 */
+	static final int UNSHALLOW = 1 << 8;
+
+	/**
 	 * Number of flag bits we keep internal for our own use. See above flags.
 	 */
-	static final int RESERVED_FLAGS = 8;
+	static final int RESERVED_FLAGS = 9;
 
 	private static final int APP_FLAGS = -1 & ~((1 << RESERVED_FLAGS) - 1);
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/TopoSortGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/TopoSortGenerator.java
index 950b0a4..4cc0fdc 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/TopoSortGenerator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/TopoSortGenerator.java
@@ -51,7 +51,9 @@ class TopoSortGenerator extends Generator {
 				break;
 			}
 			for (RevCommit p : c.getParents()) {
-				p.inDegree++;
+				if (!p.has(RevFlag.UNSHALLOW)) {
+					p.inDegree++;
+				}
 				if (firstParent) {
 					break;
 				}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificate.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificate.java
index 437abb0..b95d094 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificate.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificate.java
@@ -53,11 +53,12 @@ public enum NonceStatus {
 	private final String nonce;
 	private final NonceStatus nonceStatus;
 	private final List<ReceiveCommand> commands;
+	private final List<String> pushOptions;
 	private final String signature;
 
 	PushCertificate(String version, PushCertificateIdent pusher, String pushee,
 			String nonce, NonceStatus nonceStatus, List<ReceiveCommand> commands,
-			String signature) {
+			List<String> pushOptions, String signature) {
 		if (version == null || version.isEmpty()) {
 			throw new IllegalArgumentException(MessageFormat.format(
 					JGitText.get().pushCertificateInvalidField, VERSION));
@@ -95,6 +96,7 @@ public enum NonceStatus {
 		this.nonce = nonce;
 		this.nonceStatus = nonceStatus;
 		this.commands = commands;
+		this.pushOptions = pushOptions;
 		this.signature = signature;
 	}
 
@@ -171,6 +173,16 @@ public List<ReceiveCommand> getCommands() {
 	}
 
 	/**
+	 * Get the list of push options that were included in the push certificate.
+	 *
+	 * @return the push options, or an empty list if none.
+	 * @since 7.5
+	 */
+	public List<String> getPushOptions() {
+		return pushOptions;
+	}
+
+	/**
 	 * Get the raw signature
 	 *
 	 * @return the raw signature, consisting of the lines received between the
@@ -212,8 +224,12 @@ private StringBuilder toStringBuilder() {
 		if (pushee != null) {
 			sb.append(PUSHEE).append(' ').append(pushee).append('\n');
 		}
-		sb.append(NONCE).append(' ').append(nonce).append('\n')
-				.append('\n');
+		sb.append(NONCE).append(' ').append(nonce).append('\n');
+		for (String option : pushOptions) {
+			sb.append(PushCertificateParser.PUSH_OPTION).append(' ')
+				.append(option).append('\n');
+		}
+		sb.append('\n');
 		for (ReceiveCommand cmd : commands) {
 			sb.append(cmd.getOldId().name())
 				.append(' ').append(cmd.getNewId().name())
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateParser.java
index 463d053..c9f0dec 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateParser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateParser.java
@@ -47,6 +47,8 @@ public class PushCertificateParser {
 
 	static final String NONCE = "nonce"; //$NON-NLS-1$
 
+	static final String PUSH_OPTION = "push-option"; //$NON-NLS-1$
+
 	static final String END_CERT = "push-cert-end"; //$NON-NLS-1$
 
 	private static final String VERSION_0_1 = "0.1"; //$NON-NLS-1$
@@ -173,6 +175,7 @@ public static PushCertificate fromString(String str)
 	private final boolean enabled;
 	private final NonceGenerator nonceGenerator;
 	private final List<ReceiveCommand> commands = new ArrayList<>();
+	private final List<String> pushOptions = new ArrayList<>();
 
 	/**
 	 * <p>Constructor for PushCertificateParser.</p>
@@ -251,7 +254,8 @@ public PushCertificate build() throws IOException {
 		}
 		try {
 			return new PushCertificate(version, pusher, pushee, receivedNonce,
-					nonceStatus, Collections.unmodifiableList(commands), signature);
+					nonceStatus, Collections.unmodifiableList(commands),
+					Collections.unmodifiableList(pushOptions), signature);
 		} catch (IllegalArgumentException e) {
 			throw new IOException(e.getMessage(), e);
 		}
@@ -368,10 +372,16 @@ private void receiveHeader(StringReader reader, boolean stateless)
 					? nonceGenerator.verify(
 						receivedNonce, sentNonce(), db, stateless, nonceSlopLimit)
 					: NonceStatus.UNSOLICITED;
-			// An empty line.
-			if (!reader.read().isEmpty()) {
-				throw new PackProtocolException(
-						JGitText.get().pushCertificateInvalidHeader);
+			// Read push-option lines (if any) before the empty line separator
+			String line;
+			while (!(line = reader.read()).isEmpty()) {
+				if (line.startsWith(PUSH_OPTION + " ")) {
+					pushOptions.add(parseHeader(line, PUSH_OPTION));
+				} else {
+					// Not a push-option, should be empty line
+					throw new PackProtocolException(
+							JGitText.get().pushCertificateInvalidHeader);
+				}
 			}
 		} catch (EOFException eof) {
 			throw new PackProtocolException(
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateStore.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateStore.java
index 6bdaf0e..6a27d06 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateStore.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateStore.java
@@ -504,7 +504,7 @@ private static String buildMessage(PushCertificate cert) {
 		} else {
 			sb.append(MessageFormat.format(
 					JGitText.get().storePushCertMultipleRefs,
-					cert.getCommands().size()));
+					Integer.valueOf(cert.getCommands().size())));
 		}
 		return sb.append('\n').toString();
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java
index 6f211e0..3d9c81c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java
@@ -40,6 +40,7 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Function;
@@ -1375,6 +1376,11 @@ private void recvCommands() throws IOException {
 			if (hasCommands()) {
 				readPostCommands(pck);
 			}
+			// Verify that push options in the certificate match the post-command ones.
+			if (pushCert != null) {
+				validateCertificatePushOptions(pushCert.getPushOptions(),
+						pushOptions, commands);
+			}
 		} catch (Throwable t) {
 			discardCommands();
 			throw t;
@@ -1406,6 +1412,35 @@ private void parseShallow(String idStr) throws PackProtocolException {
 	}
 
 	/**
+	 * Validates that push options in the certificate match the post-command
+	 * push options. If they don't match, marks all commands as rejected.
+	 * <p>
+	 * This prevents tampering with signed push certificates by ensuring the
+	 * options that were signed match exactly the options sent after the
+	 * commands.
+	 *
+	 * @param certPushOptions
+	 *            the push options from the certificate
+	 * @param postCommandPushOptions
+	 *            the push options sent after commands (may be null)
+	 * @param commands
+	 *            the list of commands to reject if validation fails
+	 */
+	static void validateCertificatePushOptions(List<String> certPushOptions,
+			List<String> postCommandPushOptions, List<ReceiveCommand> commands) {
+		List<String> postOptions = postCommandPushOptions == null
+				? Collections.emptyList()
+				: postCommandPushOptions;
+		if (!Objects.equals(certPushOptions, postOptions)) {
+			// Mark all commands as rejected like in C Git.
+			for (ReceiveCommand cmd : commands) {
+				cmd.setResult(Result.REJECTED_OTHER_REASON,
+						JGitText.get().pushCertificateInconsistentPushOptions);
+			}
+		}
+	}
+
+	/**
 	 * @param in
 	 *            request stream.
 	 * @throws IOException
diff --git a/pom.xml b/pom.xml
index 016ef83..038b067 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>7.4.1-SNAPSHOT</version>
+  <version>7.5.0-SNAPSHOT</version>
 
   <name>JGit - Parent</name>
   <url>${jgit-url}</url>
@@ -118,7 +118,7 @@
 
     <project.build.outputTimestamp>${commit.time.iso}</project.build.outputTimestamp>
 
-    <jgit-last-release-version>7.3.0.202506031305-r</jgit-last-release-version>
+    <jgit-last-release-version>7.4.0.202509020913-r</jgit-last-release-version>
     <ant-version>1.10.15</ant-version>
     <apache-sshd-version>2.16.0</apache-sshd-version>
     <jsch-version>0.1.55</jsch-version>
@@ -130,25 +130,25 @@
     <commons-compress-version>1.28.0</commons-compress-version>
     <osgi-core-version>6.0.0</osgi-core-version>
     <servlet-api-version>6.1.0</servlet-api-version>
-    <jetty-version>12.1.0</jetty-version>
-    <japicmp-version>0.23.1</japicmp-version>
+    <jetty-version>12.1.3</jetty-version>
+    <japicmp-version>0.24.1</japicmp-version>
     <httpclient-version>4.5.14</httpclient-version>
     <httpcore-version>4.4.16</httpcore-version>
-    <slf4j-version>1.7.36</slf4j-version>
-    <maven-javadoc-plugin-version>3.11.2</maven-javadoc-plugin-version>
-    <gson-version>2.13.1</gson-version>
-    <bouncycastle-version>1.81</bouncycastle-version>
-    <spotbugs-maven-plugin-version>4.9.3.0</spotbugs-maven-plugin-version>
+    <slf4j-version>2.0.17</slf4j-version>
+    <maven-javadoc-plugin-version>3.12.0</maven-javadoc-plugin-version>
+    <gson-version>2.13.2</gson-version>
+    <bouncycastle-version>1.82</bouncycastle-version>
+    <spotbugs-maven-plugin-version>4.9.6.0</spotbugs-maven-plugin-version>
     <maven-project-info-reports-plugin-version>3.9.0</maven-project-info-reports-plugin-version>
     <maven-jxr-plugin-version>3.6.0</maven-jxr-plugin-version>
-    <maven-surefire-plugin-version>3.5.3</maven-surefire-plugin-version>
+    <maven-surefire-plugin-version>3.5.4</maven-surefire-plugin-version>
     <maven-surefire-report-plugin-version>${maven-surefire-plugin-version}</maven-surefire-report-plugin-version>
-    <maven-compiler-plugin-version>3.14.0</maven-compiler-plugin-version>
+    <maven-compiler-plugin-version>3.14.1</maven-compiler-plugin-version>
     <plexus-compiler-version>2.13.0</plexus-compiler-version>
-    <hamcrest-version>2.2</hamcrest-version>
-    <assertj-version>3.27.4</assertj-version>
-    <jna-version>5.17.0</jna-version>
-    <byte-buddy-version>1.17.7</byte-buddy-version>
+    <hamcrest-version>3.0</hamcrest-version>
+    <assertj-version>3.27.6</assertj-version>
+    <jna-version>5.18.1</jna-version>
+    <byte-buddy-version>1.17.8</byte-buddy-version>
 
     <!-- Properties to enable jacoco code coverage analysis -->
     <sonar.core.codeCoveragePlugin>jacoco</sonar.core.codeCoveragePlugin>
@@ -208,13 +208,13 @@
 
         <plugin>
           <artifactId>maven-clean-plugin</artifactId>
-          <version>3.4.1</version>
+          <version>3.5.0</version>
         </plugin>
 
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-shade-plugin</artifactId>
-          <version>3.6.0</version>
+          <version>3.6.1</version>
         </plugin>
 
         <plugin>
@@ -226,7 +226,7 @@
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-dependency-plugin</artifactId>
-          <version>3.8.1</version>
+          <version>3.9.0</version>
         </plugin>
 
         <plugin>
@@ -255,7 +255,7 @@
         <plugin>
           <groupId>org.codehaus.mojo</groupId>
           <artifactId>build-helper-maven-plugin</artifactId>
-          <version>3.6.0</version>
+          <version>3.6.1</version>
         </plugin>
 
         <plugin>
@@ -277,7 +277,7 @@
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-pmd-plugin</artifactId>
-          <version>3.26.0</version>
+          <version>3.27.0</version>
           <configuration>
             <inputEncoding>${project.build.sourceEncoding}</inputEncoding>
             <minimumTokens>100</minimumTokens>
@@ -372,7 +372,7 @@
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-artifact-plugin</artifactId>
-          <version>3.6.0</version>
+          <version>3.6.1</version>
           <configuration>
             <ignore>**/*cyclonedx.json</ignore>
             <reproducible>true</reproducible>
@@ -381,7 +381,7 @@
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-enforcer-plugin</artifactId>
-          <version>3.5.0</version>
+          <version>3.6.2</version>
         </plugin>
       </plugins>
     </pluginManagement>
@@ -655,7 +655,7 @@
       <plugin>
         <groupId>org.codehaus.gmavenplus</groupId>
         <artifactId>gmavenplus-plugin</artifactId>
-        <version>4.2.0</version>
+        <version>4.2.1</version>
         <dependencies>
           <dependency>
             <groupId>org.apache.groovy</groupId>
@@ -1020,7 +1020,7 @@
       <dependency>
         <groupId>org.mockito</groupId>
         <artifactId>mockito-core</artifactId>
-        <version>5.19.0</version>
+        <version>5.20.0</version>
       </dependency>
 
       <dependency>
diff --git a/tools/maven-central/Pipfile b/tools/maven-central/Pipfile
new file mode 100644
index 0000000..cffba46
--- /dev/null
+++ b/tools/maven-central/Pipfile
@@ -0,0 +1,17 @@
+[[source]]
+url = "https://pypi.org/simple"
+verify_ssl = true
+name = "pypi"
+
+[packages]
+requests = "*"
+argparse = "*"
+pyyaml = "*"
+
+[dev-packages]
+flake8 = "*"
+black = "*"
+
+[requires]
+python_version = "3.12"
+python_full_version = "3.12.9"
diff --git a/tools/maven-central/Pipfile.lock b/tools/maven-central/Pipfile.lock
new file mode 100644
index 0000000..40358c9
--- /dev/null
+++ b/tools/maven-central/Pipfile.lock
@@ -0,0 +1,339 @@
+{
+    "_meta": {
+        "hash": {
+            "sha256": "096165cac35064650a41e592a40a9d1e13ae4022a1f6d43582534f1c3bf0dbf9"
+        },
+        "pipfile-spec": 6,
+        "requires": {
+            "python_full_version": "3.12.9",
+            "python_version": "3.12"
+        },
+        "sources": [
+            {
+                "name": "pypi",
+                "url": "https://pypi.org/simple",
+                "verify_ssl": true
+            }
+        ]
+    },
+    "default": {
+        "argparse": {
+            "hashes": [
+                "sha256:62b089a55be1d8949cd2bc7e0df0bddb9e028faefc8c32038cc84862aefdd6e4",
+                "sha256:c31647edb69fd3d465a847ea3157d37bed1f95f19760b11a47aa91c04b666314"
+            ],
+            "index": "pypi",
+            "version": "==1.4.0"
+        },
+        "certifi": {
+            "hashes": [
+                "sha256:e564105f78ded564e3ae7c923924435e1daa7463faeab5bb932bc53ffae63407",
+                "sha256:f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5"
+            ],
+            "markers": "python_version >= '3.7'",
+            "version": "==2025.8.3"
+        },
+        "charset-normalizer": {
+            "hashes": [
+                "sha256:00237675befef519d9af72169d8604a067d92755e84fe76492fef5441db05b91",
+                "sha256:02425242e96bcf29a49711b0ca9f37e451da7c70562bc10e8ed992a5a7a25cc0",
+                "sha256:027b776c26d38b7f15b26a5da1044f376455fb3766df8fc38563b4efbc515154",
+                "sha256:07a0eae9e2787b586e129fdcbe1af6997f8d0e5abaa0bc98c0e20e124d67e601",
+                "sha256:0cacf8f7297b0c4fcb74227692ca46b4a5852f8f4f24b3c766dd94a1075c4884",
+                "sha256:0e78314bdc32fa80696f72fa16dc61168fda4d6a0c014e0380f9d02f0e5d8a07",
+                "sha256:0f2be7e0cf7754b9a30eb01f4295cc3d4358a479843b31f328afd210e2c7598c",
+                "sha256:13faeacfe61784e2559e690fc53fa4c5ae97c6fcedb8eb6fb8d0a15b475d2c64",
+                "sha256:14c2a87c65b351109f6abfc424cab3927b3bdece6f706e4d12faaf3d52ee5efe",
+                "sha256:1606f4a55c0fd363d754049cdf400175ee96c992b1f8018b993941f221221c5f",
+                "sha256:16a8770207946ac75703458e2c743631c79c59c5890c80011d536248f8eaa432",
+                "sha256:18343b2d246dc6761a249ba1fb13f9ee9a2bcd95decc767319506056ea4ad4dc",
+                "sha256:18b97b8404387b96cdbd30ad660f6407799126d26a39ca65729162fd810a99aa",
+                "sha256:1bb60174149316da1c35fa5233681f7c0f9f514509b8e399ab70fea5f17e45c9",
+                "sha256:1e8ac75d72fa3775e0b7cb7e4629cec13b7514d928d15ef8ea06bca03ef01cae",
+                "sha256:1ef99f0456d3d46a50945c98de1774da86f8e992ab5c77865ea8b8195341fc19",
+                "sha256:2001a39612b241dae17b4687898843f254f8748b796a2e16f1051a17078d991d",
+                "sha256:23b6b24d74478dc833444cbd927c338349d6ae852ba53a0d02a2de1fce45b96e",
+                "sha256:252098c8c7a873e17dd696ed98bbe91dbacd571da4b87df3736768efa7a792e4",
+                "sha256:257f26fed7d7ff59921b78244f3cd93ed2af1800ff048c33f624c87475819dd7",
+                "sha256:2c322db9c8c89009a990ef07c3bcc9f011a3269bc06782f916cd3d9eed7c9312",
+                "sha256:30a96e1e1f865f78b030d65241c1ee850cdf422d869e9028e2fc1d5e4db73b92",
+                "sha256:30d006f98569de3459c2fc1f2acde170b7b2bd265dc1943e87e1a4efe1b67c31",
+                "sha256:31a9a6f775f9bcd865d88ee350f0ffb0e25936a7f930ca98995c05abf1faf21c",
+                "sha256:320e8e66157cc4e247d9ddca8e21f427efc7a04bbd0ac8a9faf56583fa543f9f",
+                "sha256:34a7f768e3f985abdb42841e20e17b330ad3aaf4bb7e7aeeb73db2e70f077b99",
+                "sha256:3653fad4fe3ed447a596ae8638b437f827234f01a8cd801842e43f3d0a6b281b",
+                "sha256:3cd35b7e8aedeb9e34c41385fda4f73ba609e561faedfae0a9e75e44ac558a15",
+                "sha256:3cfb2aad70f2c6debfbcb717f23b7eb55febc0bb23dcffc0f076009da10c6392",
+                "sha256:416175faf02e4b0810f1f38bcb54682878a4af94059a1cd63b8747244420801f",
+                "sha256:41d1fc408ff5fdfb910200ec0e74abc40387bccb3252f3f27c0676731df2b2c8",
+                "sha256:42e5088973e56e31e4fa58eb6bd709e42fc03799c11c42929592889a2e54c491",
+                "sha256:4ca4c094de7771a98d7fbd67d9e5dbf1eb73efa4f744a730437d8a3a5cf994f0",
+                "sha256:511729f456829ef86ac41ca78c63a5cb55240ed23b4b737faca0eb1abb1c41bc",
+                "sha256:53cd68b185d98dde4ad8990e56a58dea83a4162161b1ea9272e5c9182ce415e0",
+                "sha256:585f3b2a80fbd26b048a0be90c5aae8f06605d3c92615911c3a2b03a8a3b796f",
+                "sha256:5b413b0b1bfd94dbf4023ad6945889f374cd24e3f62de58d6bb102c4d9ae534a",
+                "sha256:5d8d01eac18c423815ed4f4a2ec3b439d654e55ee4ad610e153cf02faf67ea40",
+                "sha256:6aab0f181c486f973bc7262a97f5aca3ee7e1437011ef0c2ec04b5a11d16c927",
+                "sha256:6cf8fd4c04756b6b60146d98cd8a77d0cdae0e1ca20329da2ac85eed779b6849",
+                "sha256:6fb70de56f1859a3f71261cbe41005f56a7842cc348d3aeb26237560bfa5e0ce",
+                "sha256:6fce4b8500244f6fcb71465d4a4930d132ba9ab8e71a7859e6a5d59851068d14",
+                "sha256:70bfc5f2c318afece2f5838ea5e4c3febada0be750fcf4775641052bbba14d05",
+                "sha256:73dc19b562516fc9bcf6e5d6e596df0b4eb98d87e4f79f3ae71840e6ed21361c",
+                "sha256:74d77e25adda8581ffc1c720f1c81ca082921329452eba58b16233ab1842141c",
+                "sha256:78deba4d8f9590fe4dae384aeff04082510a709957e968753ff3c48399f6f92a",
+                "sha256:86df271bf921c2ee3818f0522e9a5b8092ca2ad8b065ece5d7d9d0e9f4849bcc",
+                "sha256:88ab34806dea0671532d3f82d82b85e8fc23d7b2dd12fa837978dad9bb392a34",
+                "sha256:8999f965f922ae054125286faf9f11bc6932184b93011d138925a1773830bbe9",
+                "sha256:8dcfc373f888e4fb39a7bc57e93e3b845e7f462dacc008d9749568b1c4ece096",
+                "sha256:939578d9d8fd4299220161fdd76e86c6a251987476f5243e8864a7844476ba14",
+                "sha256:96b2b3d1a83ad55310de8c7b4a2d04d9277d5591f40761274856635acc5fcb30",
+                "sha256:a2d08ac246bb48479170408d6c19f6385fa743e7157d716e144cad849b2dd94b",
+                "sha256:b256ee2e749283ef3ddcff51a675ff43798d92d746d1a6e4631bf8c707d22d0b",
+                "sha256:b5e3b2d152e74e100a9e9573837aba24aab611d39428ded46f4e4022ea7d1942",
+                "sha256:b89bc04de1d83006373429975f8ef9e7932534b8cc9ca582e4db7d20d91816db",
+                "sha256:bd28b817ea8c70215401f657edef3a8aa83c29d447fb0b622c35403780ba11d5",
+                "sha256:c60e092517a73c632ec38e290eba714e9627abe9d301c8c8a12ec32c314a2a4b",
+                "sha256:c6dbd0ccdda3a2ba7c2ecd9d77b37f3b5831687d8dc1b6ca5f56a4880cc7b7ce",
+                "sha256:c6e490913a46fa054e03699c70019ab869e990270597018cef1d8562132c2669",
+                "sha256:c6f162aabe9a91a309510d74eeb6507fab5fff92337a15acbe77753d88d9dcf0",
+                "sha256:c6fd51128a41297f5409deab284fecbe5305ebd7e5a1f959bee1c054622b7018",
+                "sha256:cc34f233c9e71701040d772aa7490318673aa7164a0efe3172b2981218c26d93",
+                "sha256:cc9370a2da1ac13f0153780040f465839e6cccb4a1e44810124b4e22483c93fe",
+                "sha256:ccf600859c183d70eb47e05a44cd80a4ce77394d1ac0f79dbd2dd90a69a3a049",
+                "sha256:ce571ab16d890d23b5c278547ba694193a45011ff86a9162a71307ed9f86759a",
+                "sha256:cf1ebb7d78e1ad8ec2a8c4732c7be2e736f6e5123a4146c5b89c9d1f585f8cef",
+                "sha256:d0e909868420b7049dafd3a31d45125b31143eec59235311fc4c57ea26a4acd2",
+                "sha256:d22dbedd33326a4a5190dd4fe9e9e693ef12160c77382d9e87919bce54f3d4ca",
+                "sha256:d716a916938e03231e86e43782ca7878fb602a125a91e7acb8b5112e2e96ac16",
+                "sha256:d79c198e27580c8e958906f803e63cddb77653731be08851c7df0b1a14a8fc0f",
+                "sha256:d95bfb53c211b57198bb91c46dd5a2d8018b3af446583aab40074bf7988401cb",
+                "sha256:e28e334d3ff134e88989d90ba04b47d84382a828c061d0d1027b1b12a62b39b1",
+                "sha256:ec557499516fc90fd374bf2e32349a2887a876fbf162c160e3c01b6849eaf557",
+                "sha256:fb6fecfd65564f208cbf0fba07f107fb661bcd1a7c389edbced3f7a493f70e37",
+                "sha256:fb731e5deb0c7ef82d698b0f4c5bb724633ee2a489401594c5c88b02e6cb15f7",
+                "sha256:fb7f67a1bfa6e40b438170ebdc8158b78dc465a5a67b6dde178a46987b244a72",
+                "sha256:fd10de089bcdcd1be95a2f73dbe6254798ec1bda9f450d5828c96f93e2536b9c",
+                "sha256:fdabf8315679312cfa71302f9bd509ded4f2f263fb5b765cf1433b39106c3cc9"
+            ],
+            "markers": "python_version >= '3.7'",
+            "version": "==3.4.3"
+        },
+        "idna": {
+            "hashes": [
+                "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9",
+                "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"
+            ],
+            "markers": "python_version >= '3.6'",
+            "version": "==3.10"
+        },
+        "pyyaml": {
+            "hashes": [
+                "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c",
+                "sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a",
+                "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3",
+                "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956",
+                "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6",
+                "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c",
+                "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65",
+                "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a",
+                "sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0",
+                "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b",
+                "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1",
+                "sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6",
+                "sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7",
+                "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e",
+                "sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007",
+                "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310",
+                "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4",
+                "sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9",
+                "sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295",
+                "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea",
+                "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0",
+                "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e",
+                "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac",
+                "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9",
+                "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7",
+                "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35",
+                "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb",
+                "sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b",
+                "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69",
+                "sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5",
+                "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b",
+                "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c",
+                "sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369",
+                "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd",
+                "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824",
+                "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198",
+                "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065",
+                "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c",
+                "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c",
+                "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764",
+                "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196",
+                "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b",
+                "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00",
+                "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac",
+                "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8",
+                "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e",
+                "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28",
+                "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3",
+                "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5",
+                "sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4",
+                "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b",
+                "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf",
+                "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5",
+                "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702",
+                "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8",
+                "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788",
+                "sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da",
+                "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d",
+                "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc",
+                "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c",
+                "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba",
+                "sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f",
+                "sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917",
+                "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5",
+                "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26",
+                "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f",
+                "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b",
+                "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be",
+                "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c",
+                "sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3",
+                "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6",
+                "sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926",
+                "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0"
+            ],
+            "index": "pypi",
+            "markers": "python_version >= '3.8'",
+            "version": "==6.0.3"
+        },
+        "requests": {
+            "hashes": [
+                "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6",
+                "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf"
+            ],
+            "index": "pypi",
+            "markers": "python_version >= '3.9'",
+            "version": "==2.32.5"
+        },
+        "urllib3": {
+            "hashes": [
+                "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760",
+                "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc"
+            ],
+            "markers": "python_version >= '3.9'",
+            "version": "==2.5.0"
+        }
+    },
+    "develop": {
+        "black": {
+            "hashes": [
+                "sha256:0172a012f725b792c358d57fe7b6b6e8e67375dd157f64fa7a3097b3ed3e2175",
+                "sha256:0474bca9a0dd1b51791fcc507a4e02078a1c63f6d4e4ae5544b9848c7adfb619",
+                "sha256:154b06d618233fe468236ba1f0e40823d4eb08b26f5e9261526fde34916b9140",
+                "sha256:1b9dc70c21ef8b43248f1d86aedd2aaf75ae110b958a7909ad8463c4aa0880b0",
+                "sha256:2ab0ce111ef026790e9b13bd216fa7bc48edd934ffc4cbf78808b235793cbc92",
+                "sha256:3bec74ee60f8dfef564b573a96b8930f7b6a538e846123d5ad77ba14a8d7a64f",
+                "sha256:456386fe87bad41b806d53c062e2974615825c7a52159cde7ccaeb0695fa28fa",
+                "sha256:474b34c1342cdc157d307b56c4c65bce916480c4a8f6551fdc6bf9b486a7c4ae",
+                "sha256:77e7060a00c5ec4b3367c55f39cf9b06e68965a4f2e61cecacd6d0d9b7ec945a",
+                "sha256:846d58e3ce7879ec1ffe816bb9df6d006cd9590515ed5d17db14e17666b2b357",
+                "sha256:8e46eecf65a095fa62e53245ae2795c90bdecabd53b50c448d0a8bcd0d2e74c4",
+                "sha256:9101ee58ddc2442199a25cb648d46ba22cd580b00ca4b44234a324e3ec7a0f7e",
+                "sha256:a16b14a44c1af60a210d8da28e108e13e75a284bf21a9afa6b4571f96ab8bb9d",
+                "sha256:aaf319612536d502fdd0e88ce52d8f1352b2c0a955cc2798f79eeca9d3af0608",
+                "sha256:b756fc75871cb1bcac5499552d771822fd9db5a2bb8db2a7247936ca48f39831",
+                "sha256:c0372a93e16b3954208417bfe448e09b0de5cc721d521866cd9e0acac3c04a1f",
+                "sha256:ce41ed2614b706fd55fd0b4a6909d06b5bab344ffbfadc6ef34ae50adba3d4f7",
+                "sha256:d119957b37cc641596063cd7db2656c5be3752ac17877017b2ffcdb9dfc4d2b1",
+                "sha256:e3c1f4cd5e93842774d9ee4ef6cd8d17790e65f44f7cdbaab5f2cf8ccf22a823",
+                "sha256:e593466de7b998374ea2585a471ba90553283fb9beefcfa430d84a2651ed5933",
+                "sha256:ef69351df3c84485a8beb6f7b8f9721e2009e20ef80a8d619e2d1788b7816d47",
+                "sha256:f96b6726d690c96c60ba682955199f8c39abc1ae0c3a494a9c62c0184049a713"
+            ],
+            "index": "pypi",
+            "markers": "python_version >= '3.9'",
+            "version": "==25.9.0"
+        },
+        "click": {
+            "hashes": [
+                "sha256:9b9f285302c6e3064f4330c05f05b81945b2a39544279343e6e7c5f27a9baddc",
+                "sha256:e7b8232224eba16f4ebe410c25ced9f7875cb5f3263ffc93cc3e8da705e229c4"
+            ],
+            "markers": "python_version >= '3.10'",
+            "version": "==8.3.0"
+        },
+        "flake8": {
+            "hashes": [
+                "sha256:b9696257b9ce8beb888cdbe31cf885c90d31928fe202be0889a7cdafad32f01e",
+                "sha256:fe044858146b9fc69b551a4b490d69cf960fcb78ad1edcb84e7fbb1b4a8e3872"
+            ],
+            "index": "pypi",
+            "markers": "python_version >= '3.9'",
+            "version": "==7.3.0"
+        },
+        "mccabe": {
+            "hashes": [
+                "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325",
+                "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"
+            ],
+            "markers": "python_version >= '3.6'",
+            "version": "==0.7.0"
+        },
+        "mypy-extensions": {
+            "hashes": [
+                "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505",
+                "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"
+            ],
+            "markers": "python_version >= '3.8'",
+            "version": "==1.1.0"
+        },
+        "packaging": {
+            "hashes": [
+                "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484",
+                "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"
+            ],
+            "markers": "python_version >= '3.8'",
+            "version": "==25.0"
+        },
+        "pathspec": {
+            "hashes": [
+                "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08",
+                "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"
+            ],
+            "markers": "python_version >= '3.8'",
+            "version": "==0.12.1"
+        },
+        "platformdirs": {
+            "hashes": [
+                "sha256:abd01743f24e5287cd7a5db3752faf1a2d65353f38ec26d98e25a6db65958c85",
+                "sha256:ca753cf4d81dc309bc67b0ea38fd15dc97bc30ce419a7f58d13eb3bf14c4febf"
+            ],
+            "markers": "python_version >= '3.9'",
+            "version": "==4.4.0"
+        },
+        "pycodestyle": {
+            "hashes": [
+                "sha256:c4b5b517d278089ff9d0abdec919cd97262a3367449ea1c8b49b91529167b783",
+                "sha256:dd6bf7cb4ee77f8e016f9c8e74a35ddd9f67e1d5fd4184d86c3b98e07099f42d"
+            ],
+            "markers": "python_version >= '3.9'",
+            "version": "==2.14.0"
+        },
+        "pyflakes": {
+            "hashes": [
+                "sha256:b24f96fafb7d2ab0ec5075b7350b3d2d2218eab42003821c06344973d3ea2f58",
+                "sha256:f742a7dbd0d9cb9ea41e9a24a918996e8170c799fa528688d40dd582c8265f4f"
+            ],
+            "markers": "python_version >= '3.9'",
+            "version": "==3.4.0"
+        },
+        "pytokens": {
+            "hashes": [
+                "sha256:c9a4bfa0be1d26aebce03e6884ba454e842f186a59ea43a6d3b25af58223c044",
+                "sha256:db7b72284e480e69fb085d9f251f66b3d2df8b7166059261258ff35f50fb711b"
+            ],
+            "markers": "python_version >= '3.8'",
+            "version": "==0.1.10"
+        }
+    }
+}
diff --git a/tools/maven-central/README.md b/tools/maven-central/README.md
new file mode 100644
index 0000000..40156a5
--- /dev/null
+++ b/tools/maven-central/README.md
@@ -0,0 +1,85 @@
+# Creating a JGit release and deploying it to Maven Central
+
+## Prerequisites
+
+- you need to be a Eclipse JGit committer
+- install [jreleaser CLI](https://jreleaser.org/guide/latest/install.html)
+- install [python 3.12](https://www.python.org/)
+- install [pipenv](https://pipenv.pypa.io/en/latest/installation.html) we use below to setup a python virtualenv
+- we sign release tags and Maven artifacts using GPG.
+  Follow [Git Tools - Signing Your Work](https://git-scm.com/book/en/v2/Git-Tools-Signing-Your-Work)
+  to set this up on your computer before creating the first release.
+
+## Configure credentials for deployment to Maven Central Portal
+
+- register a user account on the [Maven Central Portal](https://central.sonatype.com/)
+- ask Eclipse Foundation to get yourself registered for the
+  [Maven Central namespace](https://central.sonatype.org/register/namespace/) `org.eclipse.jgit`
+- login to the [Maven Central Portal](https://central.sonatype.com/)
+- if you don't already have one [create a user token](https://central.sonatype.org/publish/generate-portal-token/)
+  [here](https://central.sonatype.com/usertoken)
+- add the username and token from the user token to ~/.jreleaser/config.toml
+  ```
+  JRELEASER_GITHUB_TOKEN="EMPTY"
+  JRELEASER_MAVENCENTRAL_TOKEN="maven central token"
+  JRELEASER_MAVENCENTRAL_USERNAME="maven central username"
+  ```
+- store your gpg passphrase used for GPG signing the Maven artifacts in ~/.gnupg/passphrase
+- restrict read access to the jreleaser config and the gpg passphrase file to yourself
+  ```
+  chmod 600 ~/.jreleaser/config.toml
+  chmod 600 ~/.gnupg/passphrase
+  ```
+
+## Create a JGit release and deploy it to repo.eclipse.org
+
+- Use the `tools/release.sh` script to create a new release, e.g.
+  ```
+  ./tools/release.sh v6.1.0.202203080745-r
+  ```
+  this script
+  - modifies all version identifiers in `pom.xml` files and OSGi manifests to the new release version
+  - creates a commit
+  - tags the release commit using a signed, annotated tag
+- release versions have 5 parts `major.minor.patch.buildTimestamp-qualifier`
+- since 6.8 we use UTC timezone for the buildTimestamp part of the version number, earlier we used EST.
+- `qualifier` is `m1`, `m2`, ..., for milestones, `rc1`, `rc2`, ... for release candidates and `r` for releases
+- we create all milestones and releases on a dedicated stable branch to avoid interference with
+  ongoing development on `master`. E.g. use the `stable-6.1` branch for releasing `6.1.0` and
+  subsequent patch releases like `6.1.1`.
+- push the locally created release commit to eclipse.gerrithub.io for review
+- wait for the verification build to succeed until it votes `+1` on the `Verified` label
+- review and submit the release change, then push the release tag to `eclipse.gerrithub.io`
+  ```
+  $ git push origin tag v6.1.0.202203080745-r
+  ```
+- the CI job will build the release version and deploy it to the
+  [Eclipse Maven Repository](https://repo.eclipse.org/content/groups/releases/org/eclipse/jgit/)
+
+## Deploy a JGit release to Maven Central
+
+- prepare virtualenv for `download_release.py`:
+  ```
+  $ cd tools/maven-central
+  $ pipenv --python 3.12
+  $ pipenv sync
+  ```
+- download a JGit release from repo.eclipse.org and create artifact signature files (`.asc`)
+  using your GPG signing key
+  ```
+  $ pipenv run ./download_release.py 6.1.0.202203080745-r
+  ```
+- deploy the release to maven central portal
+  ```
+  $ JRELEASER_MAVENCENTRAL_STAGE=UPLOAD jreleaser deploy
+  ```
+- check in the [Maven Central Portal](https://central.sonatype.com/publishing/deployments)
+  if the release looks good. You can download uploaded artifacts from there.
+  How to manually test a staged release is explained
+  [here](https://central.sonatype.org/publish/publish-portal-api/#manually-testing-a-deployment-bundle)
+- publish the new release
+  - by clicking "Publish" on the [portal deployment page](https://central.sonatype.com/publishing/deployments)
+  - or by running
+    ```
+    $ JRELEASER_MAVENCENTRAL_STAGE=PUBLISH jreleaser deploy
+    ```
diff --git a/tools/maven-central/deploy.rb b/tools/maven-central/deploy.rb
deleted file mode 100755
index ece1337..0000000
--- a/tools/maven-central/deploy.rb
+++ /dev/null
@@ -1,74 +0,0 @@
-#!/usr/bin/env ruby
-require 'rubygems'
-require 'highline/import'
-
-def run(args)
-  system(*args)
-end
-
-def deploy_jar(artifact, version, prefix)
-  pom = "#{artifact}-#{version}.pom"
-  binary = "#{artifact}-#{version}.jar"
-  javadoc = "#{artifact}-#{version}-javadoc.jar"
-  sources = "#{artifact}-#{version}-sources.jar"
-
-  run prefix + ["-DpomFile=#{pom}", "-Dfile=#{binary}"]
-  run prefix + ["-DpomFile=#{pom}", "-Dfile=#{sources}",
-                   "-Dclassifier=sources"]
-  run prefix + ["-DpomFile=#{pom}", "-Dfile=#{javadoc}",
-                   "-Dclassifier=javadoc"]
-end
-
-def deploy_parent(version, prefix)
-  pom = "org.eclipse.jgit-parent-#{version}.pom"
-  run prefix + ["-DpomFile=#{pom}", "-Dfile=#{pom}"]
-end
-
-def deploy_sh(artifact, version, prefix)
-  pom = "#{artifact}-#{version}.pom"
-  sh = "#{artifact}-#{version}.sh"
-  run prefix + ["-DpomFile=#{pom}", "-Dfile=#{sh}", "-Dpackaging=sh"]
-end
-
-def get_passphrase(prompt="Enter your GPG Passphrase")
-   ask(prompt) {|q| q.echo = false}
-end
-
-version = ARGV[0].freeze
-if version =~ /\A(\d+\.\d+\.\d+)\.(\d{12})-(m\d|rc\d|r)\Z/
-   printf "version %s qualifier %s classifier %s\n", $1, $2, $3
-else
-   printf "invalid version %s\n", version
-   abort
-end
-
-url = 'https://oss.sonatype.org/service/local/staging/deploy/maven2/'
-repositoryId = 'sonatype-nexus-staging'
-puts "gpg passphrase ?"
-passphrase = get_passphrase()
-
-group = 'org.eclipse.jgit'
-artifacts = [group,
-             group + '.ant',
-             group + '.archive',
-             group + '.gpg.bc',
-             group + '.http.apache',
-             group + '.http.server',
-             group + '.junit',
-             group + '.junit.http',
-             group + '.junit.ssh',
-             group + '.lfs',
-             group + '.lfs.server',
-             group + '.pgm',
-             group + '.ssh.apache',
-             group + '.ssh.apache.agent',
-             group + '.ssh.jsch',
-             group + '.ui']
-
-prefix = ["mvn", "gpg:sign-and-deploy-file", "-Dgpg.passphrase=#{passphrase}",
-          "-Durl=#{url}", "-DrepositoryId=#{repositoryId}"]
-deploy_parent(version, prefix)
-artifacts.each do |artifact|
-  deploy_jar(artifact, version, prefix)
-end
-deploy_sh('org.eclipse.jgit.pgm', version, prefix)
diff --git a/tools/maven-central/download.rb b/tools/maven-central/download.rb
deleted file mode 100755
index b3c4e3d..0000000
--- a/tools/maven-central/download.rb
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env ruby
-version = ARGV[0].freeze
-if version =~ /\A(\d+\.\d+\.\d+)\.(\d{12})-(m\d|rc\d|r)\Z/
-   printf "version %s qualifier %s classifier %s\n", $1, $2, $3
-else
-   printf "invalid version %s\n", version
-   abort
-end
-
-group = 'org.eclipse.jgit'
-artifacts = [group,
-             group + '.ant',
-             group + '.archive',
-             group + '.gpg.bc',
-             group + '.http.apache',
-             group + '.http.server',
-             group + '.junit',
-             group + '.junit.http',
-             group + '.junit.ssh',
-             group + '.lfs',
-             group + '.lfs.server',
-             group + '.pgm',
-             group + '.ssh.apache',
-             group + '.ssh.apache.agent',
-             group + '.ssh.jsch',
-             group + '.ui']
-
-puts 'Deleting current files'
-`rm -fr *.jar *.sh *.pom`
-
-puts 'Downloading org.eclipse.jgit-parent'
-`curl -s https://repo.eclipse.org/content/repositories/jgit-releases/org/eclipse/jgit/#{group}-parent/#{version}/#{group}-parent-#{version}.pom -o #{group}-parent-#{version}.pom`
-
-artifacts.each {|artifact|
-  puts "Downloading #{artifact}-#{version}.jar"
-  `curl -s https://repo.eclipse.org/content/repositories/jgit-releases/org/eclipse/jgit/#{artifact}/#{version}/#{artifact}-#{version}.jar -o #{artifact}-#{version}.jar`
-  `curl -s https://repo.eclipse.org/content/repositories/jgit-releases/org/eclipse/jgit/#{artifact}/#{version}/#{artifact}-#{version}.pom -o #{artifact}-#{version}.pom`
-  `curl -s https://repo.eclipse.org/content/repositories/jgit-releases/org/eclipse/jgit/#{artifact}/#{version}/#{artifact}-#{version}-javadoc.jar -o #{artifact}-#{version}-javadoc.jar`
-  `curl -s https://repo.eclipse.org/content/repositories/jgit-releases/org/eclipse/jgit/#{artifact}/#{version}/#{artifact}-#{version}-sources.jar -o #{artifact}-#{version}-sources.jar`
-}
-
-puts "Downloading org.eclipse.jgit.pgm-#{version}.sh"
-`curl -s https://repo.eclipse.org/content/repositories/jgit-releases/org/eclipse/jgit/#{group}.pgm/#{version}/#{group}.pgm-#{version}.sh -o #{group}.pgm-#{version}.sh`
diff --git a/tools/maven-central/download_release.py b/tools/maven-central/download_release.py
new file mode 100755
index 0000000..4ce44bc
--- /dev/null
+++ b/tools/maven-central/download_release.py
@@ -0,0 +1,226 @@
+#!/usr/bin/env python3
+import argparse
+import os
+import pathlib
+import requests
+import subprocess
+import xml.etree.ElementTree as ET
+import yaml
+from concurrent.futures import ThreadPoolExecutor
+from functools import partial
+
+
+BASE_REPO_URL = "https://repo.eclipse.org/content/groups/releases"
+JGIT_GROUP_ID = "org.eclipse.jgit"
+JGIT_PARENT_ARTIFACT_ID = "org.eclipse.jgit-parent"
+DOWNLOAD_DIR = "staging-deploy"
+
+
+class Dumper(yaml.Dumper):
+    def increase_indent(self, flow=False, *args, **kwargs):
+        return super().increase_indent(flow=flow, indentless=False)
+
+
+def fetch_and_parse_pom(url):
+    """Fetches and parses a Maven POM file from a URL."""
+    print(f"Fetching parent POM from {url}")
+    try:
+        response = requests.get(url)
+        response.raise_for_status()
+        return ET.fromstring(response.content)
+    except requests.exceptions.RequestException as e:
+        print(f"Error fetching POM: {e}")
+        exit(1)
+
+
+def get_pom_info(pom_root):
+    """Extracts GAV and modules from a parsed POM XML."""
+    ns = {"m": "http://maven.apache.org/POM/4.0.0"}
+
+    group_id_elem = pom_root.find("m:groupId", ns)
+    if group_id_elem is None:
+        group_id_elem = pom_root.find("m:parent/m:groupId", ns)
+
+    version_elem = pom_root.find("m:version", ns)
+    if version_elem is None:
+        version_elem = pom_root.find("m:parent/m:version", ns)
+
+    java_version_elem = None
+    for prop in pom_root.findall("m:properties", ns):
+        java_version_elem = prop.find("m:java.version", ns)
+        if java_version_elem is not None:
+            break
+
+    info = {
+        "groupId": group_id_elem.text,
+        "artifactId": pom_root.find("m:artifactId", ns).text,
+        "version": version_elem.text,
+        "java_version": java_version_elem.text,
+        "modules": [
+            module.text for module in pom_root.findall("m:modules/m:module", ns)
+        ],
+    }
+    return info
+
+
+def download_artifacts(artifacts_to_download):
+    """Downloads all artifacts for the given GAVs into the DOWNLOAD_DIR."""
+    for artifact in artifacts_to_download:
+        group_path = artifact["groupId"].replace(".", "/")
+        base_url = f"{BASE_REPO_URL}/{group_path}/{artifact['artifactId']}/{artifact['version']}"
+        group_download_dir = f"{DOWNLOAD_DIR}/{group_path}"
+        artifact_download_dir = (
+            f"{group_download_dir}/{artifact['artifactId']}/{artifact['version']}"
+        )
+        if not os.path.exists(artifact_download_dir):
+            os.makedirs(artifact_download_dir)
+
+        filenames = [f"{artifact['artifactId']}-{artifact['version']}.pom"]
+        if "parent" not in artifact["artifactId"]:
+            filenames.extend(
+                [
+                    f"{artifact['artifactId']}-{artifact['version']}.jar",
+                    f"{artifact['artifactId']}-{artifact['version']}-sources.jar",
+                    f"{artifact['artifactId']}-{artifact['version']}-javadoc.jar",
+                    f"{artifact['artifactId']}-{artifact['version']}-cyclonedx.json",
+                    f"{artifact['artifactId']}-{artifact['version']}.sh",
+                ]
+            )
+
+        print(
+            f"\nDownloading artifacts for {artifact['groupId']}:{artifact['artifactId']}:{artifact['version']}"
+        )
+        download_task = partial(download_file, artifact_download_dir, base_url)
+        with ThreadPoolExecutor(max_workers=8) as executor:
+            executor.map(download_task, filenames)
+
+
+def download_file(artifact_download_dir, base_url, filename):
+    """Downloads the given file from base_url to artifact_download_dir in a Maven repository layout"""
+    url = f"{base_url}/{filename}"
+    local_path = os.path.join(artifact_download_dir, filename)
+
+    try:
+        with requests.get(url, stream=True) as r:
+            if r.status_code == 200:
+                with open(local_path, "wb") as f:
+                    for chunk in r.iter_content(chunk_size=8192):
+                        f.write(chunk)
+                print(f"  Downloaded: {filename}")
+            elif r.status_code == 404:
+                pass
+            else:
+                print(
+                    f"  Warning: Failed to download {filename} (Status: {r.status_code})"
+                )
+    except requests.exceptions.RequestException as e:
+        print(f"  Error downloading {filename}: {e}")
+
+
+def sign_artifacts(passphrase_file):
+    """GPG signs all artifacts under DOWNLOAD_DIR"""
+    artifact_dir = pathlib.Path(DOWNLOAD_DIR)
+    print("Signing artifacts")
+    for file in artifact_dir.rglob("*"):
+        if file.is_file() and file.suffix in {".jar", ".pom", ".sh", ".json"}:
+            try:
+                subprocess.run(
+                    [
+                        "gpg",
+                        "--batch",
+                        "--yes",
+                        "--pinentry-mode",
+                        "loopback",
+                        "--passphrase-file",
+                        passphrase_file,
+                        "--armor",
+                        "--detach-sign",
+                        str(file),
+                    ],
+                    check=True,
+                )
+                print(f"  Signed: {str(file)}")
+            except subprocess.CalledProcessError as e:
+                print(f"Error signing {file}: {e.stderr}")
+
+
+def jreleaser_set_values(jgit_version, java_version):
+    """Generates jreleaser.yml from template and sets values extracted from parent pom"""
+    if not jgit_version:
+        print("jgit_version undefined")
+    if not java_version:
+        print("java_version undefined")
+
+    with open("jreleaser.yml.template", "r") as f:
+        data = yaml.safe_load(f)
+
+    data["project"]["version"] = jgit_version
+    data["project"]["languages"]["java"]["version"] = java_version
+
+    with open("jreleaser.yml", "w") as f:
+        yaml.dump(data, f, sort_keys=False, Dumper=Dumper)
+
+
+def main():
+    """Main execution function."""
+    parser = argparse.ArgumentParser(
+        description="""Download artifacts of a JGit release from repo.eclipse.org.""",
+        usage="%(prog)s <version> [--passphrase_file <path to passphrase file> (default: ~/.gnupg/passphrase)]",
+    )
+    # 'version' as a positional argument
+    parser.add_argument(
+        "version", help="The JGit version to process (e.g., 5.13.4.202507202350-r)."
+    )
+    parser.add_argument(
+        "--passphrase_file",
+        help="Path to file containing gpg passphrase",
+        default=f"{pathlib.Path.home()}/.gnupg/passphrase",
+    )
+    args = parser.parse_args()
+    jgit_version = args.version
+    passphrase_file = args.passphrase_file
+
+    group_path = JGIT_GROUP_ID.replace(".", "/")
+    filename = f"{JGIT_PARENT_ARTIFACT_ID}-{jgit_version}.pom"
+    parent_pom_url = f"{BASE_REPO_URL}/{group_path}/{JGIT_PARENT_ARTIFACT_ID}/{jgit_version}/{filename}"
+
+    pom_root = fetch_and_parse_pom(parent_pom_url)
+    parent_info = get_pom_info(pom_root)
+
+    if parent_info["version"] != jgit_version:
+        print(
+            f"Warning: The version in the POM ('{parent_info['version']}') does not match the requested version ('{jgit_version}')."
+        )
+        print("Proceeding with the version found in the POM file.")
+
+    print("\n--- Project Information ---")
+    print(f"GroupId:    {parent_info['groupId']}")
+    print(f"ArtifactId: {parent_info['artifactId']}")
+    print(f"Version:    {parent_info['version']}")
+    print(f"Found {len(parent_info['modules'])} modules.")
+
+    artifacts_to_process = []
+    artifacts_to_process.append(
+        {
+            "groupId": parent_info["groupId"],
+            "artifactId": parent_info["artifactId"],
+            "version": parent_info["version"],
+        }
+    )
+    for module in parent_info["modules"]:
+        if not module.endswith((".test", ".coverage", ".benchmarks")):
+            artifacts_to_process.append(
+                {
+                    "groupId": parent_info["groupId"],
+                    "artifactId": module,
+                    "version": parent_info["version"],
+                }
+            )
+
+    download_artifacts(artifacts_to_process)
+    sign_artifacts(passphrase_file)
+    jreleaser_set_values(parent_info["version"], parent_info["java_version"])
+
+
+if __name__ == "__main__":
+    main()
diff --git a/tools/maven-central/jreleaser.yml.template b/tools/maven-central/jreleaser.yml.template
new file mode 100644
index 0000000..a926cc4
--- /dev/null
+++ b/tools/maven-central/jreleaser.yml.template
@@ -0,0 +1,50 @@
+# JReleaser configuration template for JGit release promotion to Maven Central.
+project:
+  name: 'org.eclipse.jgit'
+  version:
+  description: 'JGit - The Java Git Implementation'
+  license: 'BSD-3-Clause'
+  authors:
+    - 'The JGit Committers'
+  languages:
+    java:
+      groupId: org.eclipse.jgit
+      version:
+  links:
+    homepage: https://github.com/eclipse-jgit/jgit/
+
+release:
+  github:
+    skipRelease: true
+
+signing:
+  active: RELEASE
+  armored: true
+  mode: COMMAND
+
+deploy:
+  maven:
+    mavenCentral:
+      org.eclipse.jgit:
+        active: RELEASE
+        url: https://central.sonatype.com/api/v1/publisher
+        snapshotSupported: false
+        authorization: BASIC
+        applyMavenCentralRules: true
+        sign: false
+        verifyPom: false
+        namespace: 'org.eclipse.jgit'
+        stagingRepositories:
+          - staging-deploy
+        connectTimeout: 20
+        readTimeout: 60
+        retryDelay: 60
+        maxRetries: 100
+
+files:
+  globs:
+    - pattern: staging-deploy/**/*.jar
+    - pattern: staging-deploy/**/*-cyclonedx.json
+    - pattern: staging-deploy/**/*-javadoc.jar
+    - pattern: staging-deploy/**/*-sources.jar
+    - pattern: staging-deploy/**/*.sh