Merge branch 'master' into stable-7.5

* master:
  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
  Fix hamcrest version to 3.0.0 in target platform
  Add InterruptTimer Tests
  Update slf4j to 2.0.17
  Update org.hamcrest to 3.0.0
  DfsPackFile: asObjectIdSet() when only inclusion is needed.
  DfsObjDatabase: read reftables from midx covered packs
  Update jetty to 12.1.3
  Fix signed push failing when using push options
  DfsReader/PackFile: Move bitmap inclusion check to the packfile
  Use generic Map interface in signature of internal API methods
  Allow to discover bitmap on disk created after the packfile
  DfsPackCompactor: add toPrune method
  MidxPackList: Helper to get packs/midx and overlaps between them
  DfsPackFileMidx: make the class public
  [ssh] Fix order of keys in pubkey auth if an agent is used
  Update bytebuddy to 1.17.8
  Update jetty to 12.1.2
  DfsPackCompactor: Move #autoAdd to the tests (its only caller)
  DfsPackFileMidx: add #getAllCoveredPacks() method
  DfsGarbageCollector: handle pack lists with multipack indexes
  DfsPackFileMidx: Return bitmap from any underlying pack
  midx: caller chooses the order of packs in the midx
  DfsObjDatabase: make #setUseMultipackIndex public
  MidxPackFilterTest: Remove unused import
  DfsObjDatabase: move pack mangling to helper MidxPackFilter
  Document how to test a release staged in Maven Central Portal
  DfsMidxWriter: helper class to create a pack with an midx
  DfsObjDatabase: make useMultipackIndex getter/setter protected
  Update Maven plugins
  DfsObjDatabase: introduce multipack index
  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: I6fba55292e9948ae74808e4c0883742b4048c2ac
diff --git a/WORKSPACE b/WORKSPACE
index b690564..644e283 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -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(
@@ -198,8 +200,8 @@
 
 maven_jar(
     name = "hamcrest",
-    artifact = "org.hamcrest:hamcrest:2.2",
-    sha1 = "1820c0968dba3a11a1b30669bb1f01978a91dedc",
+    artifact = "org.hamcrest:hamcrest:3.0",
+    sha1 = "8fd9b78a8e6a6510a078a9e30e9e86a6035cfaf7",
 )
 
 maven_jar(
@@ -214,18 +216,18 @@
     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(
@@ -240,54 +242,54 @@
     sha1 = "48b8230771e573b54ce6e867a9001e75977fe78e",
 )
 
-JETTY_VER = "12.1.1"
+JETTY_VER = "12.1.3"
 
 maven_jar(
     name = "jetty-servlet",
     artifact = "org.eclipse.jetty.ee10:jetty-ee10-servlet:" + JETTY_VER,
-    sha1 = "28e6e238d77f94d71714945a7d064fedcefa6918",
+    sha1 = "b2709dddac048fb03735c9ea85673dbfcec8bdfc",
 )
 
 maven_jar(
     name = "jetty-security",
     artifact = "org.eclipse.jetty:jetty-security:" + JETTY_VER,
-    sha1 = "089c47852a9ac216e66b2d3d2e12b8721b9b8ca2",
+    sha1 = "f40cb316ef0e9a168f59eaf0b2d0440b7adcb1f8",
 )
 
 maven_jar(
     name = "jetty-server",
     artifact = "org.eclipse.jetty:jetty-server:" + JETTY_VER,
-    sha1 = "4a34529a4737bcc4868186914a72a097edee1a52",
+    sha1 = "7741aafcd7d6dc718d04d4cfb883982e361d6577",
 )
 
 maven_jar(
     name = "jetty-session",
     artifact = "org.eclipse.jetty:jetty-session:" + JETTY_VER,
-    sha1 = "7db390e7856c75e001b1ade978e27397dcbc97eb",
+    sha1 = "0837f081a357a2c835a96ceaa09445921a33b0e9",
 )
 
 maven_jar(
     name = "jetty-http",
     artifact = "org.eclipse.jetty:jetty-http:" + JETTY_VER,
-    sha1 = "4c087942e5cdaf4b716146312f9ff4c5a1e289d9",
+    sha1 = "3822fb03ec85e4157cf68c1ac661b5d06fd19b25",
 )
 
 maven_jar(
     name = "jetty-io",
     artifact = "org.eclipse.jetty:jetty-io:" + JETTY_VER,
-    sha1 = "f9efcb4d69ee757721c5f0f3bf4cd55b48a44b48",
+    sha1 = "2c46bf53f41e40df72ff457c40553da2ace7b956",
 )
 
 maven_jar(
     name = "jetty-util",
     artifact = "org.eclipse.jetty:jetty-util:" + JETTY_VER,
-    sha1 = "582a66cbad2b577ad11b7945e434bd5732c1e45d",
+    sha1 = "9606f6cc440419f62214ceaa2c8ab4b312baf6be",
 )
 
 maven_jar(
     name = "jetty-util-ajax",
     artifact = "org.eclipse.jetty:jetty-util-ajax:" + JETTY_VER,
-    sha1 = "81d03427b838dfc994c072eadd69e8a8a3802c0d",
+    sha1 = "d0b0a631434ca2cf5a913d2852893779bfecebf8",
 )
 
 BOUNCYCASTLE_VER = "1.82"
diff --git a/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF
index 1dde9aa..0157b21 100644
--- a/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF
@@ -14,5 +14,5 @@
  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.core;version="[1.1.0,3.0.0)",
+ org.hamcrest;version="[3.0.0,4.0.0)",
  org.junit;version="[4.13,5.0.0)"
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 44a76a1..10544b6 100644
--- a/org.eclipse.jgit.gpg.bc.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.gpg.bc.test/META-INF/MANIFEST.MF
@@ -8,7 +8,6 @@
 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)",
@@ -18,6 +17,7 @@
  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.http.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF
index d1d82a8..8c4b4cb 100644
--- a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF
@@ -8,8 +8,6 @@
 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)",
@@ -50,6 +48,7 @@
  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.lfs.server.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF
index b7bd24d..cbe0465 100644
--- a/org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF
@@ -45,7 +45,7 @@
  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.core;version="[1.1.0,3.0.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.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF
index f460a8c..cc1b4cc 100644
--- a/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF
@@ -24,7 +24,7 @@
  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.core;version="[1.1.0,3.0.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.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.target/jgit-4.32.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.32.target
deleted file mode 100644
index a91976c..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.32.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.32" sequenceNumber="1759825554">
-  <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-06"/>
-    </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/"/>
-    </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.20.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.18.1</version>
-          <type>jar</type>
-        </dependency>
-        <dependency>
-          <groupId>net.java.dev.jna</groupId>
-          <artifactId>jna-platform</artifactId>
-          <version>5.18.1</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.1</version>
-          <type>jar</type>
-        </dependency>
-        <dependency>
-          <groupId>org.eclipse.jetty</groupId>
-          <artifactId>jetty-http</artifactId>
-          <version>12.1.1</version>
-          <type>jar</type>
-        </dependency>
-        <dependency>
-          <groupId>org.eclipse.jetty</groupId>
-          <artifactId>jetty-io</artifactId>
-          <version>12.1.1</version>
-          <type>jar</type>
-        </dependency>
-        <dependency>
-          <groupId>org.eclipse.jetty</groupId>
-          <artifactId>jetty-security</artifactId>
-          <version>12.1.1</version>
-          <type>jar</type>
-        </dependency>
-        <dependency>
-          <groupId>org.eclipse.jetty</groupId>
-          <artifactId>jetty-server</artifactId>
-          <version>12.1.1</version>
-          <type>jar</type>
-        </dependency>
-        <dependency>
-          <groupId>org.eclipse.jetty</groupId>
-          <artifactId>jetty-session</artifactId>
-          <version>12.1.1</version>
-          <type>jar</type>
-        </dependency>
-        <dependency>
-          <groupId>org.eclipse.jetty</groupId>
-          <artifactId>jetty-util</artifactId>
-          <version>12.1.1</version>
-          <type>jar</type>
-        </dependency>
-        <dependency>
-          <groupId>org.eclipse.jetty</groupId>
-          <artifactId>jetty-util-ajax</artifactId>
-          <version>12.1.1</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.2</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.82</version>
-          <type>jar</type>
-        </dependency>
-        <dependency>
-          <groupId>org.bouncycastle</groupId>
-          <artifactId>bcprov-jdk18on</artifactId>
-          <version>1.82</version>
-          <type>jar</type>
-        </dependency>
-        <dependency>
-          <groupId>org.bouncycastle</groupId>
-          <artifactId>bcpkix-jdk18on</artifactId>
-          <version>1.82</version>
-          <type>jar</type>
-        </dependency>
-        <dependency>
-          <groupId>org.bouncycastle</groupId>
-          <artifactId>bcutil-jdk18on</artifactId>
-          <version>1.82</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.6</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.19.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.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 220a42a..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="1759825555">
-  <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.20.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.18.1</version>
-          <type>jar</type>
-        </dependency>
-        <dependency>
-          <groupId>net.java.dev.jna</groupId>
-          <artifactId>jna-platform</artifactId>
-          <version>5.18.1</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.1</version>
-          <type>jar</type>
-        </dependency>
-        <dependency>
-          <groupId>org.eclipse.jetty</groupId>
-          <artifactId>jetty-http</artifactId>
-          <version>12.1.1</version>
-          <type>jar</type>
-        </dependency>
-        <dependency>
-          <groupId>org.eclipse.jetty</groupId>
-          <artifactId>jetty-io</artifactId>
-          <version>12.1.1</version>
-          <type>jar</type>
-        </dependency>
-        <dependency>
-          <groupId>org.eclipse.jetty</groupId>
-          <artifactId>jetty-security</artifactId>
-          <version>12.1.1</version>
-          <type>jar</type>
-        </dependency>
-        <dependency>
-          <groupId>org.eclipse.jetty</groupId>
-          <artifactId>jetty-server</artifactId>
-          <version>12.1.1</version>
-          <type>jar</type>
-        </dependency>
-        <dependency>
-          <groupId>org.eclipse.jetty</groupId>
-          <artifactId>jetty-session</artifactId>
-          <version>12.1.1</version>
-          <type>jar</type>
-        </dependency>
-        <dependency>
-          <groupId>org.eclipse.jetty</groupId>
-          <artifactId>jetty-util</artifactId>
-          <version>12.1.1</version>
-          <type>jar</type>
-        </dependency>
-        <dependency>
-          <groupId>org.eclipse.jetty</groupId>
-          <artifactId>jetty-util-ajax</artifactId>
-          <version>12.1.1</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.2</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.82</version>
-          <type>jar</type>
-        </dependency>
-        <dependency>
-          <groupId>org.bouncycastle</groupId>
-          <artifactId>bcprov-jdk18on</artifactId>
-          <version>1.82</version>
-          <type>jar</type>
-        </dependency>
-        <dependency>
-          <groupId>org.bouncycastle</groupId>
-          <artifactId>bcpkix-jdk18on</artifactId>
-          <version>1.82</version>
-          <type>jar</type>
-        </dependency>
-        <dependency>
-          <groupId>org.bouncycastle</groupId>
-          <artifactId>bcutil-jdk18on</artifactId>
-          <version>1.82</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.6</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.19.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 95494e8..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="1759825554">
+<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>
@@ -103,49 +107,49 @@
         <dependency>
           <groupId>org.eclipse.jetty.ee10</groupId>
           <artifactId>jetty-ee10-servlet</artifactId>
-          <version>12.1.1</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-http</artifactId>
-          <version>12.1.1</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-io</artifactId>
-          <version>12.1.1</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-security</artifactId>
-          <version>12.1.1</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-server</artifactId>
-          <version>12.1.1</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-session</artifactId>
-          <version>12.1.1</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-util</artifactId>
-          <version>12.1.1</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-util-ajax</artifactId>
-          <version>12.1.1</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>
@@ -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>
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 dd06ccd..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="1759825554">
+<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>
@@ -103,49 +107,49 @@
         <dependency>
           <groupId>org.eclipse.jetty.ee10</groupId>
           <artifactId>jetty-ee10-servlet</artifactId>
-          <version>12.1.1</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-http</artifactId>
-          <version>12.1.1</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-io</artifactId>
-          <version>12.1.1</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-security</artifactId>
-          <version>12.1.1</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-server</artifactId>
-          <version>12.1.1</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-session</artifactId>
-          <version>12.1.1</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-util</artifactId>
-          <version>12.1.1</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-util-ajax</artifactId>
-          <version>12.1.1</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>
@@ -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>
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 e6cf65a..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="1759825554">
+<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>
@@ -103,49 +107,49 @@
         <dependency>
           <groupId>org.eclipse.jetty.ee10</groupId>
           <artifactId>jetty-ee10-servlet</artifactId>
-          <version>12.1.1</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-http</artifactId>
-          <version>12.1.1</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-io</artifactId>
-          <version>12.1.1</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-security</artifactId>
-          <version>12.1.1</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-server</artifactId>
-          <version>12.1.1</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-session</artifactId>
-          <version>12.1.1</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-util</artifactId>
-          <version>12.1.1</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-util-ajax</artifactId>
-          <version>12.1.1</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>
@@ -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>
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 3765962..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="1759825554">
+<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>
@@ -103,49 +107,49 @@
         <dependency>
           <groupId>org.eclipse.jetty.ee10</groupId>
           <artifactId>jetty-ee10-servlet</artifactId>
-          <version>12.1.1</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-http</artifactId>
-          <version>12.1.1</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-io</artifactId>
-          <version>12.1.1</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-security</artifactId>
-          <version>12.1.1</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-server</artifactId>
-          <version>12.1.1</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-session</artifactId>
-          <version>12.1.1</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-util</artifactId>
-          <version>12.1.1</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-util-ajax</artifactId>
-          <version>12.1.1</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>
@@ -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>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.38.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.38.target
index 71ac500..c0f3dd9 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.38.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.38.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.38" sequenceNumber="1759825922">
+<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"/>
@@ -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.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"/>
@@ -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>
@@ -103,49 +107,49 @@
         <dependency>
           <groupId>org.eclipse.jetty.ee10</groupId>
           <artifactId>jetty-ee10-servlet</artifactId>
-          <version>12.1.1</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-http</artifactId>
-          <version>12.1.1</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-io</artifactId>
-          <version>12.1.1</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-security</artifactId>
-          <version>12.1.1</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-server</artifactId>
-          <version>12.1.1</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-session</artifactId>
-          <version>12.1.1</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-util</artifactId>
-          <version>12.1.1</version>
+          <version>12.1.3</version>
           <type>jar</type>
         </dependency>
         <dependency>
           <groupId>org.eclipse.jetty</groupId>
           <artifactId>jetty-util-ajax</artifactId>
-          <version>12.1.1</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>
@@ -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>
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 2f2f0b3..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
@@ -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"
 	}
 }
 
@@ -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.1"
+		version = "12.1.3"
 	}
 	dependency {
 		groupId = "org.eclipse.jetty"
 		artifactId = "jetty-http"
-		version = "12.1.1"
+		version = "12.1.3"
 	}
 	dependency {
 		groupId = "org.eclipse.jetty"
 		artifactId = "jetty-io"
-		version = "12.1.1"
+		version = "12.1.3"
 	}
 	dependency {
 		groupId = "org.eclipse.jetty"
 		artifactId = "jetty-security"
-		version = "12.1.1"
+		version = "12.1.3"
 	}
 	dependency {
 		groupId = "org.eclipse.jetty"
 		artifactId = "jetty-server"
-		version = "12.1.1"
+		version = "12.1.3"
 	}
 	dependency {
 		groupId = "org.eclipse.jetty"
 		artifactId = "jetty-session"
-		version = "12.1.1"
+		version = "12.1.3"
 	}
 	dependency {
 		groupId = "org.eclipse.jetty"
 		artifactId = "jetty-util"
-		version = "12.1.1"
+		version = "12.1.3"
 	}
 	dependency {
 		groupId = "org.eclipse.jetty"
 		artifactId = "jetty-util-ajax"
-		version = "12.1.1"
+		version = "12.1.3"
 	}
 	dependency {
 		groupId = "jakarta.servlet"
@@ -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
index cfa3682..7601cc4 100644
--- 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
@@ -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.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]
diff --git a/org.eclipse.jgit.packaging/pom.xml b/org.eclipse.jgit.packaging/pom.xml
index 8215c2c..301ed91 100644
--- a/org.eclipse.jgit.packaging/pom.xml
+++ b/org.eclipse.jgit.packaging/pom.xml
@@ -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/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.ssh.apache.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.ssh.apache.test/META-INF/MANIFEST.MF
index c74fa22..c10f613 100644
--- a/org.eclipse.jgit.ssh.apache.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ssh.apache.test/META-INF/MANIFEST.MF
@@ -8,8 +8,8 @@
 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)",
@@ -42,6 +42,7 @@
  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/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/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 2443823..5823845 100644
--- a/org.eclipse.jgit.ssh.jsch.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ssh.jsch.test/META-INF/MANIFEST.MF
@@ -8,7 +8,6 @@
 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.5.0,7.6.0)",
  org.eclipse.jgit.internal.storage.file;version="[7.5.0,7.6.0)",
@@ -18,6 +17,7 @@
  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.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.test/META-INF/MANIFEST.MF
index 9689f43..c724d35 100644
--- a/org.eclipse.jgit.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.test/META-INF/MANIFEST.MF
@@ -8,8 +8,6 @@
 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)",
@@ -83,6 +81,8 @@
  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/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
index 998ea0d..3fd4e2c 100644
--- 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
@@ -29,6 +29,7 @@
 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;
@@ -36,6 +37,7 @@
 
 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;
@@ -302,36 +304,35 @@ 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);
-		long packOneSize = packOne.getPackDescription().getFileSize(PACK);
 		DfsPackFile packTwo = findPack(o2);
 		long packTwoSize = packTwo.getPackDescription().getFileSize(PACK);
 		DfsPackFile packThree = findPack(o3);
+		long packThreeSize = packThree.getPackDescription().getFileSize(PACK);
 
-		// Packs have sequential names (pack-NN-INSERT) and midx uses pack-name
-		// order. We rely on that to know the pack offsets
 		try (DfsReader ctx = db.getObjectDatabase().newReader()) {
 			long posOne = midx.findOffset(ctx, o1);
 			DfsPackFileMidx.DfsPackOffset po = midx.getOffsetCalculator()
 					.decode(posOne);
 			assertEquals(12, po.getPackOffset());
-			assertEquals(0, po.getPackStart());
+			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(packOneSize, po.getPackStart());
+			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(packOneSize + packTwoSize, po.getPackStart());
+			assertEquals(0, po.getPackStart());
 			assertEquals(packThree.getPackDescription(),
 					po.getPack().getPackDescription());
 
@@ -420,15 +421,15 @@ public void midx_findAllFromPack() throws Exception {
 					true);
 			assertEquals(3, allFromPack.size());
 
-			// Objects are in (pack, offset) order (i.e. pack)
+			// Objects are in (pack, offset) order (i.e. reverse pack insert)
 			DfsObjectToPack oneToPack = allFromPack.get(0);
-			assertEquals(midx.findOffset(ctx, o1), oneToPack.getOffset());
+			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, o3), threeToPack.getOffset());
+			assertEquals(midx.findOffset(ctx, o1), threeToPack.getOffset());
 		}
 	}
 
@@ -442,7 +443,7 @@ public void midx_findAllFromPack_withBase() throws Exception {
 		ObjectId o6 = writePackWithRandomBlob(600);
 
 		DfsPackFile[] packs = db.getObjectDatabase().getPacks();
-		// Packs are in reverse insertion order
+		// Packs are in reverse insertion order (o6 -> o1)
 		DfsPackFileMidx midxBase = writeMultipackIndex(
 				Arrays.copyOfRange(packs, 3, 6), null);
 		DfsPackFileMidx midxTip = writeMultipackIndex(
@@ -461,24 +462,25 @@ public void midx_findAllFromPack_withBase() throws Exception {
 					otps, true);
 			assertEquals(6, allFromPack.size());
 
-			// Objects are in (pack, offset) order (i.e. pack)
+			// 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, o1), oneToPack.getOffset());
+			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, o3), threeToPack.getOffset());
+			assertEquals(midxTip.findOffset(ctx, o1), threeToPack.getOffset());
 
 			DfsObjectToPack fourToPack = allFromPack.get(3);
-			assertEquals(midxTip.findOffset(ctx, o4), fourToPack.getOffset());
+			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, o6), sixToPack.getOffset());
+			assertEquals(midxTip.findOffset(ctx, o4), sixToPack.getOffset());
 		}
 	}
 
@@ -842,6 +844,92 @@ public void midx_fillRepresentation_withBase() throws Exception {
 	}
 
 	@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);
@@ -1067,16 +1155,22 @@ 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 {
-		Map<String, PackIndex> forMidx = new HashMap<>(packs.length);
-		Map<String, DfsPackDescription> requiredPacks = new HashMap<>(
+		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));
-				requiredPacks.put(pack.getPackDescription().getPackName(),
+				descByName.put(pack.getPackDescription().getPackName(),
 						pack.getPackDescription());
 			}
 		}
@@ -1087,7 +1181,7 @@ private DfsPackFileMidx writeMultipackIndex(DfsPackFile[] packs,
 			MultiPackIndexWriter.Result midxStats = w
 					.write(NullProgressMonitor.INSTANCE, out, forMidx);
 			desc.setCoveredPacks(midxStats.packNames().stream()
-					.map(requiredPacks::get).toList());
+					.map(descByName::get).toList());
 			desc.addFileExt(PackExt.MULTI_PACK_INDEX);
 		}
 		db.getObjectDatabase().commitPack(List.of(desc), null);
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/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 7af3f54..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,7 +287,8 @@ 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);
@@ -313,7 +318,8 @@ public void jgit_resolve_noMatches_last() 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);
@@ -334,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);
@@ -371,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);
@@ -426,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);
@@ -442,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);
@@ -472,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 26353d8..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;
@@ -46,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();
@@ -75,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();
@@ -114,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)
@@ -138,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/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.test/tst/org/eclipse/jgit/util/io/InterruptTimerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/InterruptTimerTest.java
new file mode 100644
index 0000000..68de7d6
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/InterruptTimerTest.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2025, NVIDIA CORPORATION
+ *
+ * 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.util.io;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class InterruptTimerTest {
+	private static final int MULTIPLIER = 1; // Increase if tests get flaky
+	private static final int BUFFER = 5; // Increase if tests get flaky
+	private static final int REPEATS = 100; // Increase to stress test more
+
+	private static final int TOO_LONG = 3 * MULTIPLIER + BUFFER;
+	private static final int SHORT_ENOUGH = 1 * MULTIPLIER;
+	private static final int TIMEOUT_LONG_ENOUGH = TOO_LONG;
+	private static final int TIMEOUT_TOO_SHORT = SHORT_ENOUGH;
+
+	private InterruptTimer timer;
+
+	@Before
+	public void setUp() {
+		timer = new InterruptTimer();
+	}
+
+	@After
+	public void tearDown() {
+		timer.terminate();
+		for (Thread t : active())
+			assertFalse(t instanceof InterruptTimer.AlarmThread);
+	}
+
+	@Test
+	public void testShortEnough() {
+		int interrupted = 0;
+		try {
+			timer.begin(TIMEOUT_LONG_ENOUGH);
+			Thread.sleep(SHORT_ENOUGH);
+			timer.end();
+		} catch (InterruptedException e) {
+			interrupted++;
+		}
+		assertEquals("Was Not Interrupted", interrupted, 0);
+	}
+
+	@Test
+	public void testTooLong() {
+		int interrupted = 0;
+		try {
+			timer.begin(TIMEOUT_TOO_SHORT);
+			Thread.sleep(TOO_LONG);
+			timer.end();
+		} catch (InterruptedException e) {
+			interrupted++;
+		}
+		assertEquals("Was Interrupted", interrupted, 1);
+	}
+
+	@Test
+	public void testNotInterruptedAfterEnd() {
+		int interrupted = 0;
+		try {
+			timer.begin(TIMEOUT_LONG_ENOUGH);
+			Thread.sleep(SHORT_ENOUGH);
+			timer.end();
+			Thread.sleep(TIMEOUT_LONG_ENOUGH * 3);
+		} catch (InterruptedException e) {
+			interrupted++;
+		}
+		assertEquals("Was Not Interrupted Even After End", interrupted, 0);
+	}
+
+	@Test
+	public void testRestartBeforeTimeout() {
+		int interrupted = 0;
+		try {
+			timer.begin(TIMEOUT_LONG_ENOUGH * 2);
+			Thread.sleep(SHORT_ENOUGH);
+			timer.end();
+			timer.begin(TIMEOUT_LONG_ENOUGH);
+			Thread.sleep(SHORT_ENOUGH);
+			timer.end();
+		} catch (InterruptedException e) {
+			interrupted++;
+		}
+		assertEquals("Was Not Interrupted Even When Restarted Before Timeout", interrupted, 0);
+	}
+
+	@Test
+	public void testSecondExpiresBeforeFirst() {
+		int interrupted = 0;
+		try {
+			timer.begin(TIMEOUT_LONG_ENOUGH * 3);
+			Thread.sleep(SHORT_ENOUGH);
+			timer.end();
+			timer.begin(TIMEOUT_TOO_SHORT);
+			Thread.sleep(TOO_LONG);
+			timer.end();
+		} catch (InterruptedException e) {
+			interrupted++;
+		}
+		assertEquals("Was Interrupted Even When Second Timeout Expired Before First", interrupted, 1);
+	}
+
+	@Test
+	public void testRepeatedShortEnough() {
+		int interrupted = 0;
+		for (int i = 0; i < REPEATS; i++) {
+			try {
+				timer.begin(TIMEOUT_LONG_ENOUGH);
+				Thread.sleep(SHORT_ENOUGH);
+				timer.end();
+			} catch (InterruptedException e) {
+				interrupted++;
+			}
+		}
+		assertEquals("Was Never Interrupted", interrupted, 0);
+	}
+
+	@Test
+	public void testRepeatedTooLong() {
+		int interrupted = 0;
+		for (int i = 0; i < REPEATS; i++) {
+			try {
+				timer.begin(TIMEOUT_TOO_SHORT);
+				Thread.sleep(TOO_LONG);
+				timer.end();
+			} catch (InterruptedException e) {
+				Thread.currentThread().interrupt();
+				interrupted++;
+			}
+		}
+		assertEquals("Was always Interrupted", interrupted, REPEATS);
+	}
+
+	@Test
+	public void testRepeatedShortThanTooLong() {
+		int interrupted = 0;
+		for (int i = 0; i < REPEATS; i++) {
+			try {
+				timer.begin(TIMEOUT_LONG_ENOUGH);
+				Thread.sleep(SHORT_ENOUGH);
+				timer.end();
+			} catch (InterruptedException e) {
+				interrupted++;
+			}
+		}
+		assertEquals("Was Not Interrupted Early", interrupted, 0);
+		try {
+			timer.begin(TIMEOUT_TOO_SHORT);
+			Thread.sleep(TOO_LONG);
+			timer.end();
+		} catch (InterruptedException e) {
+			interrupted++;
+		}
+		assertEquals("Was Interrupted On Long", interrupted, 1);
+	}
+
+	private static List<Thread> active() {
+		Thread[] all = new Thread[16];
+		int n = Thread.currentThread().getThreadGroup().enumerate(all);
+		while (n == all.length) {
+			all = new Thread[all.length * 2];
+			n = Thread.currentThread().getThreadGroup().enumerate(all);
+		}
+		return Arrays.asList(all).subList(0, n);
+	}
+}
diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF
index f0cf71d..d760003 100644
--- a/org.eclipse.jgit/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit/META-INF/MANIFEST.MF
@@ -103,9 +103,12 @@
    org.eclipse.jgit.junit.http,
    org.eclipse.jgit.http.server,
    org.eclipse.jgit.lfs,
+   org.eclipse.jgit.lfs.test,
    org.eclipse.jgit.pgm,
    org.eclipse.jgit.pgm.test,
-   org.eclipse.jgit.ssh.apache",
+   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.5.0";
   x-friends:="org.eclipse.jgit.junit,
    org.eclipse.jgit.test,
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/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/DfsPackFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java
index 3aacdc2..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;
@@ -251,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;
@@ -1204,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
index acdbd7e..742fe6c 100644
--- 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
@@ -14,6 +14,7 @@
 
 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;
@@ -35,7 +36,9 @@
 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;
 
@@ -45,7 +48,7 @@
  * <p>
  * It uses the position in the multipack index of the objects as their "offset".
  */
-final class DfsPackFileMidx extends DfsPackFile {
+public final class DfsPackFileMidx extends DfsPackFile {
 
 	private static final int REF_POSITION = 0;
 
@@ -146,18 +149,54 @@ public PackIndex getPackIndex(DfsReader ctx) {
 	}
 
 	@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) {
-		// TODO(ifrade): Implement this
+	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);
@@ -193,6 +232,25 @@ public List<DfsPackFile> getCoveredPacks() {
 	}
 
 	/**
+	 * 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
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/midx/PackIndexMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/PackIndexMerger.java
index 81577a8..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;
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/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/org.eclipse.jgit/src/org/eclipse/jgit/util/io/InterruptTimer.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/InterruptTimer.java
index 888b8fb..e717412 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/InterruptTimer.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/InterruptTimer.java
@@ -151,6 +151,7 @@ protected void finalize() throws Throwable {
 	static final class AlarmState implements Runnable {
 		private Thread callingThread;
 
+		private int lastTimeout;
 		private long deadline;
 
 		private boolean terminated;
@@ -163,7 +164,7 @@ static final class AlarmState implements Runnable {
 		public synchronized void run() {
 			while (!terminated && callingThread.isAlive()) {
 				try {
-					if (0 < deadline) {
+					if (deadline > 0) {
 						final long delay = deadline - now();
 						if (delay <= 0) {
 							deadline = 0;
@@ -172,7 +173,9 @@ public synchronized void run() {
 							wait(delay);
 						}
 					} else {
-						wait(1000);
+						// When the timer is not running, avoid waking up more than once a second
+						wait(lastTimeout == 0 ? 1000 : lastTimeout);
+						lastTimeout = 0;
 					}
 				} catch (InterruptedException e) {
 					// Treat an interrupt as notice to examine state.
@@ -185,15 +188,21 @@ synchronized void begin(int timeout) {
 				throw new IllegalStateException(JGitText.get().timerAlreadyTerminated);
 			callingThread = Thread.currentThread();
 			deadline = now() + timeout;
-			notifyAll();
+			if (lastTimeout != timeout) {
+				boolean isNotify = lastTimeout == 0 || lastTimeout > timeout;
+				lastTimeout = timeout;
+				if (isNotify) {
+					notifyAll();
+				} // else avoid the expensive notify when the runloop will already timeout in time
+			}
 		}
 
 		synchronized void end() {
-			if (0 == deadline)
+			if (0 == deadline) {
 				Thread.interrupted();
-			else
+			} else {
 				deadline = 0;
-			notifyAll();
+			}
 		}
 
 		synchronized void terminate() {
diff --git a/pom.xml b/pom.xml
index a75f6f4..038b067 100644
--- a/pom.xml
+++ b/pom.xml
@@ -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.1</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>
+    <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.3.0</spotbugs-maven-plugin-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>
+    <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.7</byte-buddy-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>
diff --git a/tools/maven-central/README.md b/tools/maven-central/README.md
index 04f115e..40156a5 100644
--- a/tools/maven-central/README.md
+++ b/tools/maven-central/README.md
@@ -75,6 +75,8 @@
   ```
 - 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